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 +4/−3
- src/Nix/Delegate.hs +87/−43
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 ()