packages feed

nix-delegate 1.0.0 → 1.0.1

raw patch · 2 files changed

+91/−46 lines, 2 filesdep +bytestringdep ~foldldep ~turtlePVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring

Dependency ranges changed: foldl, turtle

API changes (from Hackage documentation)

Files

nix-delegate.cabal view
@@ -1,5 +1,5 @@ Name: nix-delegate-Version: 1.0.0+Version: 1.0.1 Cabal-Version: >=1.8.0.2 Build-Type: Simple License: Apache-2.0@@ -13,12 +13,13 @@     Hs-Source-Dirs: src     Exposed-Modules: Nix.Delegate     Build-Depends: base                 >= 4.7.0.0  && < 5+                 , bytestring           >= 0.10.8.1 && < 1.0                  , neat-interpolation                  < 0.4                  , optparse-applicative >= 0.13.2.0 && < 0.15-                 , foldl                               < 1.4+                 , foldl                               < 1.5                  , managed              >= 1.0.3    && < 1.1                  , text                                < 1.3-                 , turtle               >= 1.3.0    && < 1.5+                 , turtle               >= 1.3.0    && < 1.6     GHC-Options: -Wall  Executable nix-delegate
src/Nix/Delegate.hs view
@@ -3,7 +3,7 @@ {-# LANGUAGE QuasiQuotes        #-} {-# LANGUAGE RecordWildCards    #-} -{-| This modules provides a basic library API to @nix-delegate@'s functionality+{-| This module provides a basic library API to @nix-delegate@'s functionality -}  module Nix.Delegate@@ -21,6 +21,7 @@ import           Control.Applicative       (empty, many, (<**>), (<|>)) import           Control.Exception         (SomeException) import           Control.Monad+import           Control.Monad.IO.Class    (MonadIO) import           Control.Monad.Managed     (MonadManaged) import qualified Data.Foldable             as Foldable import           Data.Maybe@@ -32,6 +33,7 @@  import qualified Control.Exception import qualified Control.Foldl             as Foldl+import qualified Data.ByteString.Lazy import qualified Data.Text import qualified NeatInterpolation import qualified Options.Applicative       as Options@@ -137,8 +139,9 @@ renderCmd (Command cmd args) = Turtle.format (s%" "%s) cmd (Data.Text.intercalate " " args)  canSudo :: Command -> Bool-canSudo (Command "nix-build" _) = True-canSudo _                       = False+canSudo (Command command _) = Turtle.filename path == "nix-build"+  where+    path = Turtle.fromText command  -- | @main@ used by the @delegate@ executable main :: IO ()@@ -153,8 +156,10 @@             )     delegate options -exchangeKeys :: Text -> IO ()-exchangeKeys host = do+exchangeKeys :: FilePath -> Text -> IO ()+exchangeKeys key host = do+  let key' = Turtle.format fp key+   -- When performing a distributed build you need to share a key pair   -- (both the public and private key) with the machine you're   -- deploying to (or from). Both machines must store the same private@@ -182,7 +187,7 @@      1. Check if you can log into the remote machine by running: -        $ ssh $host+        $ ssh -i $key' $host      2. If you can log in, then check if you have permission to `sudo` without a        password by running the following command on the remote machine:@@ -198,14 +203,14 @@   let openssl :: Turtle.Format a a       openssl =           "$(nix-build --no-out-link \"<nixpkgs>\" -A libressl)/bin/openssl"-  let fmt = "ssh "%s%" '"+  let fmt = "ssh -i "%fp%" "%s%" '"               % "test -e "%fp%" || "               % "sudo sh -c \""                   % "(umask 277 && "%openssl%" genrsa -out "%fp%" 2048) && "                   % openssl%" rsa -in "%fp%" -pubout > "%fp               % "\""           % "'"-  let cmd = Turtle.format fmt host privateKey privateKey privateKey publicKey+  let cmd = Turtle.format fmt key host privateKey privateKey privateKey publicKey   Control.Exception.handle handler0 (Turtle.shells cmd empty)    let mirror path = Turtle.runManaged $ do@@ -217,7 +222,7 @@                   Turtle.procs "rsync"                       [ "--archive"                       , "--checksum"-                      , "--rsh", "ssh"+                      , "--rsh", Turtle.format ("ssh -i "%fp) key                       , "--rsync-path", "sudo rsync"                       , Turtle.format (s%":"%fp) host path                       , Turtle.format fp localPath@@ -234,7 +239,7 @@      1. Check if you can log into the remote machine by running: -        $ ssh $host+        $ ssh -i $key' $host      2. If you can log in, then check if you have permission to `sudo` without a        password by running the following command on the remote machine:@@ -256,31 +261,35 @@            liftIO (Control.Exception.handle handler1 download) -          exitCode <- Turtle.shell "sudo -n true 2>/dev/null" empty+          new <- liftIO . Data.ByteString.Lazy.readFile . Data.Text.unpack $+            Turtle.format fp localPath -          -- NB: path shouldn't is a FilePath and won't have any-          -- newlines, so this should be okay-          Turtle.err (Turtle.unsafeTextToLine $ Turtle.format ("[+] Installing: "%fp) path)+          old <- liftIO . Data.ByteString.Lazy.readFile . Data.Text.unpack $+            Turtle.format fp path -          case exitCode of-              ExitFailure _ -> do-                  Turtle.err ""-                  Turtle.err "    This will prompt you for your `sudo` password"-              _             -> do-                  return ()+          if new == old+              then do+                  let same = Turtle.format ("[+] Unchanged: "%fp) path+                  mapM_ Turtle.err (Turtle.Line.textToLines same)+              else do+                  -- NB: path shouldn't is a FilePath and won't have any+                  -- newlines, so this should be okay+                  Turtle.err (Turtle.unsafeTextToLine $ Turtle.format ("[+] Installing: "%fp) path) -          let install =-                  Turtle.procs "sudo"-                      [ "mv"-                      , Turtle.format fp localPath-                      , Turtle.format fp path-                      ]-                      empty-          let handler2 :: SomeException -> IO ()-              handler2 e = do-                  let pathText      = Turtle.format fp path-                  let exceptionText = Data.Text.pack (show e)-                  let msg           = [NeatInterpolation.text|+                  warnSudo++                  let install =+                          Turtle.procs "sudo"+                              [ "mv"+                              , Turtle.format fp localPath+                              , Turtle.format fp path+                              ]+                              empty+                  let handler2 :: SomeException -> IO ()+                      handler2 e = do+                          let pathText      = Turtle.format fp path+                          let exceptionText = Data.Text.pack (show e)+                          let msg           = [NeatInterpolation.text| [x] Could not install: $pathText      Debugging tips:@@ -293,9 +302,9 @@      Original error: $exceptionText |]-                  Turtle.die msg+                          Turtle.die msg -          liftIO (Control.Exception.handle handler2 install)+                  liftIO (Control.Exception.handle handler2 install)    mirror privateKey   mirror publicKey@@ -303,13 +312,44 @@ delegateShared     :: MonadManaged managed => OptArgs -> managed (Text, SomeException -> IO a) delegateShared OptArgs{..}  = do+    home <- Turtle.home+    let key'      = fromMaybe (home </> ".ssh/id_rsa") key     let os'       = case os of [] -> [X86_64_Linux]; _ -> os     let os''      = Data.Text.intercalate "," (Foldable.toList (fmap renderOS os'))     let feature'  = Data.Text.intercalate "," feature     let cores'    = fromMaybe 1 cores -    liftIO (exchangeKeys host)+    isDaemon <- maybe False (== "daemon") <$> Turtle.need "NIX_REMOTE" +    let sudo | isDaemon && canSudo cmd = "sudo"+             | otherwise               = ""++    host' <-+      if isDaemon && not (Data.Text.any (== '@') host)+      then do+        mUser <- Turtle.need "USER"+        case mUser of+            Nothing   -> Turtle.die [NeatInterpolation.text|+[x] You must set the `USER` environment variable in order for `nix-delegate` to+    work in a multi-user Nix installation+|]+            Just user -> return (user <> "@" <> host)+      else return host++    {-  Do a test @ssh@ command in order to prompt the user to recognize the+        host if the host is not known++        Use @sudo@ if we are in multi-user mode since the @root@ user will be+        initiating the build and therefore the @root@ user needs to authorize+        the known host+    -}+    Turtle.err "[+] Testing SSH access"+    if sudo == "sudo" then warnSudo else return ()+    let testSSH = s%" ssh -i "%fp%" "%s%" :"+    Turtle.shells (Turtle.format testSSH sudo key' host') Turtle.stdin++    liftIO (exchangeKeys key' host')+     let debuggingTips = [NeatInterpolation.text|     Debugging tips: @@ -321,12 +361,10 @@ |]      remoteSystemsFile <- Turtle.mktempfile "/tmp" "remote-systems.conf"-    home <- Turtle.home-    let key' = fromMaybe (home </> ".ssh/id_rsa") key     let line =             Turtle.format                 (s%" "%s%" "%fp%" "%d%" 1 "%s)-                host+                host'                 os''                 key'                 cores'@@ -375,11 +413,6 @@     $debuggingTips |] -    isDaemon <- maybe False (== "daemon") <$> Turtle.need "NIX_REMOTE"--    let sudo | isDaemon && canSudo cmd = "sudo"-             | otherwise               = ""-     let renderedCmd = renderCmd cmd     let pfxcmd = Turtle.format           (s %@@ -428,3 +461,14 @@ delegateStream options = do     (command, _) <- delegateShared options     Turtle.inshell command empty++warnSudo :: MonadIO io => io ()+warnSudo = do+    exitCode <- Turtle.shell "sudo -n true 2>/dev/null" empty++    case exitCode of+        ExitFailure _ -> do+            Turtle.err ""+            Turtle.err "    This will prompt you for your `sudo` password"+        _             -> do+            return ()