packages feed

cachix 0.3.7 → 0.3.8

raw patch · 14 files changed

+175/−78 lines, 14 files

Files

CHANGELOG.md view
@@ -7,6 +7,33 @@  ## Unreleased +## [0.3.8] - 2020-06-03++### Added++- `cachix push --omit-deriver` if you'd like not to reveal Deriver field to Cachix++### Changed++- Retries are now exponential to offload the server a bit++### Fixed++- #308: Test failure due to dependency bump++- Pretty print exceptions++- Create nixos directory before checking if it's writable++- A number of error messaging improvements:++  * Don't suggest creating a cache when generating keypair as it's the wrong order++  * #290: Explain what is going on when there's no signing key++  * #262: improve instructions when on NixOS and the user is untrusted++ ## [0.3.7] - 2020-03-12  ### Added@@ -183,7 +210,10 @@  - Initial release @domenkozar -[Unreleased]: https://github.com/cachix/cachix/compare/v0.3.0...HEAD+[Unreleased]: https://github.com/cachix/cachix/compare/v0.3.7...HEAD+[0.3.7]: https://github.com/cachix/cachix/compare/v0.3.6...v0.3.7+[0.3.6]: https://github.com/cachix/cachix/compare/v0.3.5...v0.3.6+[0.3.5]: https://github.com/cachix/cachix/compare/v0.3.4...v0.3.5 [0.3.4]: https://github.com/cachix/cachix/compare/v0.3.3...v0.3.4 [0.3.3]: https://github.com/cachix/cachix/compare/v0.3.2...v0.3.3 [0.3.2]: https://github.com/cachix/cachix/compare/v0.3.1...v0.3.2
cachix.cabal view
@@ -1,9 +1,10 @@ cabal-version:      2.2 name:               cachix-version: 0.3.7+version: 0.3.8 license:            Apache-2.0 license-file:       LICENSE copyright:          2018 Domen Kožar+category:           Nix maintainer:         domen@enlambda.com author:             Domen Kožar homepage:           https://github.com/cachix/cachix#readme
src/Cachix/Client/Commands.hs view
@@ -15,7 +15,7 @@ import qualified Cachix.Api as Api import Cachix.Api.Error import Cachix.Client.Config-  ( BinaryCacheConfig (..),+  ( BinaryCacheConfig (BinaryCacheConfig),     Config (..),     mkConfig,     writeConfig,@@ -24,7 +24,7 @@ import Cachix.Client.Env (Env (..)) import Cachix.Client.Exception (CachixException (..)) import Cachix.Client.HumanSize (humanSize)-import Cachix.Client.InstallationMode+import qualified Cachix.Client.InstallationMode as InstallationMode import qualified Cachix.Client.NixConf as NixConf import Cachix.Client.NixVersion (assertNixVersion) import Cachix.Client.OptionsParser@@ -40,13 +40,11 @@   ) import Cachix.Client.Servant import qualified Cachix.Types.SigningKeyCreate as SigningKeyCreate-import Control.Concurrent (threadDelay) import Control.Exception.Safe (handle, throwM) import Control.Retry (RetryStatus (rsIterNumber)) import Crypto.Sign.Ed25519 import qualified Data.ByteString.Base64 as B64-import Data.List (isSuffixOf, reverse)-import Data.Maybe (fromJust)+import Data.List (isSuffixOf) import Data.String.Here import qualified Data.Text as T import Network.HTTP.Types (status401, status404)@@ -58,20 +56,14 @@ import System.Directory (doesFileExist) import System.Environment (lookupEnv) import System.FSNotify-import System.IO (hIsTerminalDevice, stdin)+import System.IO (hIsTerminalDevice)  authtoken :: Env -> Text -> IO () authtoken env token = do   -- TODO: check that token actually authenticates!   writeConfig (configPath (cachixoptions env)) $ case config env of-    Just config -> config {authToken = Token (toS token)}+    Just cfg -> cfg {authToken = Token (toS token)}     Nothing -> mkConfig token-  putStrLn-    ( [hereLit|-Continue by creating a binary cache at https://cachix.org-  |] ::-        Text-    )  create :: Env -> Text -> IO () create _ _ =@@ -79,7 +71,7 @@  generateKeypair :: Env -> Text -> IO () generateKeypair Env {config = Nothing} _ = throwIO $ NoConfig "Start with visiting https://cachix.org and copying the token to $ cachix authtoken <token>"-generateKeypair env@Env {config = Just config} name = do+generateKeypair env@Env {config = Just cfg} name = do   (PublicKey pk, sk) <- createKeypair   let signingKey = exportSigningKey $ SigningKey sk       signingKeyCreate = SigningKeyCreate.SigningKeyCreate (toS $ B64.encode pk)@@ -87,11 +79,11 @@   -- we first validate if key can be added to the binary cache   (_ :: NoContent) <-     escalate <=< (`runClientM` clientenv env) $-      Api.createKey (cachixBCClient name) (authToken config) signingKeyCreate+      Api.createKey (cachixBCClient name) (authToken cfg) signingKeyCreate   -- if key was successfully added, write it to the config   -- TODO: warn if binary cache with the same key already exists   writeConfig (configPath (cachixoptions env)) $-    config {binaryCaches = binaryCaches config <> [bcc]}+    cfg {binaryCaches = binaryCaches cfg <> [bcc]}   putStrLn     ( [iTrim| Secret signing key has been saved in the file above. To populate@@ -125,7 +117,7 @@ accessDeniedBinaryCache name =   AccessDeniedBinaryCache $ "You don't seem to have API access to binary cache " <> name -use :: Env -> Text -> UseOptions -> IO ()+use :: Env -> Text -> InstallationMode.UseOptions -> IO () use env name useOptions = do   -- 1. get cache public key   res <- (`runClientM` clientenv env) $ Api.get (cachixBCClient name) (envToToken env)@@ -137,18 +129,18 @@       | otherwise -> throwM err     Right binaryCache -> do       () <- escalateAs UnsupportedNixVersion =<< assertNixVersion-      user <- getUser+      user <- InstallationMode.getUser       nc <- NixConf.read NixConf.Global-      isTrusted <- isTrustedUser $ NixConf.readLines (catMaybes [nc]) NixConf.isTrustedUsers+      isTrusted <- InstallationMode.isTrustedUser $ NixConf.readLines (catMaybes [nc]) NixConf.isTrustedUsers       isNixOS <- doesFileExist "/etc/NIXOS"       let nixEnv =-            NixEnv-              { isRoot = user == "root",-                isTrusted = isTrusted,-                isNixOS = isNixOS+            InstallationMode.NixEnv+              { InstallationMode.isRoot = user == "root",+                InstallationMode.isTrusted = isTrusted,+                InstallationMode.isNixOS = isNixOS               }-      addBinaryCache (config env) binaryCache useOptions $-        fromMaybe (getInstallationMode nixEnv) (useMode useOptions)+      InstallationMode.addBinaryCache (config env) binaryCache useOptions $+        fromMaybe (InstallationMode.getInstallationMode nixEnv) (InstallationMode.useMode useOptions)  -- TODO: lots of room for performance improvements push :: Env -> PushArguments -> IO ()@@ -215,9 +207,30 @@       maybeBCSK = case config env of         Nothing -> Nothing         Just c -> Config.secretKey <$> head (matches c)-  -- TODO: better error msg-  escalateAs FatalError $ parseSigningKeyLenient $ fromJust $ maybeBCSK <|> toS <$> maybeEnvSK <|> panic "You need to: export CACHIX_SIGNING_KEY=XXX"+  signingKey <- case maybeBCSK <|> toS <$> maybeEnvSK of+    Just key -> return key+    Nothing -> throwIO $ NoSigningKey msg+  escalateAs FatalError $ parseSigningKeyLenient signingKey+  where+    msg :: Text+    msg =+      [iTrim|+Signing key not found.  +It is generated by `cachix generate-keypair <name>` and stored in ~/config/cachix/cachix.dhall++There are a few options why this happened:++a) You haven't generated signing key yet for your cache ++b) You have the key but you're pushing from a different machine. +   You can set it via $CACHIX_SIGNING_KEY environment variable.++c) You've lost the signing key. In that case it's best to delete the cache and start again.+   Note that everyone that configured the binary cache will have to do it again to set the new+   public key.+      |]+ retryText :: RetryStatus -> Text retryText retrystatus =   if rsIterNumber retrystatus == 0@@ -237,7 +250,8 @@         -- we append newline instead of putStrLn due to https://github.com/haskell/text/issues/242         putStr $ retryText retrystatus <> "compressing and pushing " <> storePath <> " (" <> humanSize (fromIntegral size) <> ")\n",       onDone = pass,-      withXzipCompressor = defaultWithXzipCompressorWithLevel (compressionLevel opts)+      withXzipCompressor = defaultWithXzipCompressorWithLevel (compressionLevel opts),+      Cachix.Client.Push.omitDeriver = Cachix.Client.OptionsParser.omitDeriver opts     }  pushStorePath :: Env -> PushOptions -> Text -> Text -> IO ()
src/Cachix/Client/Config.hs view
@@ -14,7 +14,6 @@ import Cachix.Client.Config.Orphans () import Dhall hiding (Text) import Dhall.Pretty (prettyExpr)-import GHC.Generics (Generic) import Protolude import Servant.Auth.Client import System.Directory
src/Cachix/Client/Env.hs view
@@ -10,7 +10,6 @@ import Cachix.Client.OptionsParser (CachixOptions (..)) import Cachix.Client.Store (Store, openStore) import Cachix.Client.URI (getBaseUrl)-import Control.Concurrent.Async (Async, async) import Data.Version (showVersion) import Network.HTTP.Client   ( ManagerSettings,@@ -38,15 +37,15 @@   store <- async openStore   -- make sure path to the config is passed as absolute to dhall logic   canonicalConfigPath <- canonicalizePath (configPath rawcachixoptions)-  let cachixoptions = rawcachixoptions {configPath = canonicalConfigPath}-  config <- readConfig $ configPath cachixoptions+  let cachixOptions = rawcachixoptions {configPath = canonicalConfigPath}+  cfg <- readConfig $ configPath cachixOptions   manager <- newTlsManagerWith customManagerSettings-  let clientenv = mkClientEnv manager $ getBaseUrl (host cachixoptions)+  let clientEnv = mkClientEnv manager $ getBaseUrl (host cachixOptions)   return     Env-      { config = config,-        clientenv = clientenv,-        cachixoptions = cachixoptions,+      { config = cfg,+        clientenv = clientEnv,+        cachixoptions = cachixOptions,         storeAsync = store       } 
src/Cachix/Client/Exception.hs view
@@ -8,6 +8,7 @@   | MustBeRoot Text   | NixOSInstructions Text   | AmbiguousInput Text+  | NoSigningKey Text   | NoInput Text   | NoConfig Text   | NetRcParseError Text@@ -19,5 +20,17 @@   deriving (Show, Typeable)  instance Exception CachixException where-  displayException (NixOSInstructions txt) = toS txt-  displayException e = show e+  displayException (UnsupportedNixVersion s) = toS s+  displayException (UserEnvNotSet s) = toS s+  displayException (MustBeRoot s) = toS s+  displayException (NixOSInstructions s) = toS s+  displayException (AmbiguousInput s) = toS s+  displayException (NoInput s) = toS s+  displayException (NoConfig s) = toS s+  displayException (NoSigningKey s) = toS s+  displayException (NetRcParseError s) = toS s+  displayException (NarStreamingError _ s) = toS s+  displayException (NarHashMismatch s) = toS s+  displayException (DeprecatedCommand s) = toS s+  displayException (AccessDeniedBinaryCache s) = toS s+  displayException (BinaryCacheNotFound s) = toS s
src/Cachix/Client/InstallationMode.hs view
@@ -13,7 +13,7 @@   ) where -import Cachix.Api as Api+import qualified Cachix.Api as Api import Cachix.Client.Config (Config) import Cachix.Client.Exception (CachixException (..)) import qualified Cachix.Client.NetRc as NetRc@@ -38,6 +38,7 @@   = Install NixConf.NixConfLoc   | WriteNixOS   | UntrustedRequiresSudo+  | UntrustedNixOS   deriving (Show, Eq)  data UseOptions@@ -59,19 +60,49 @@ toString (Install NixConf.Local) = "user-nixconf" toString WriteNixOS = "nixos" toString UntrustedRequiresSudo = "untrusted-requires-sudo"+toString UntrustedNixOS = "untrusted-nixos"  getInstallationMode :: NixEnv -> InstallationMode getInstallationMode nixenv   | isNixOS nixenv && isRoot nixenv = WriteNixOS   | not (isNixOS nixenv) && isRoot nixenv = Install NixConf.Global   | isTrusted nixenv = Install NixConf.Local+  | isNixOS nixenv = UntrustedNixOS   | otherwise = UntrustedRequiresSudo  -- | Add a Binary cache to nix.conf, print nixos config or fail addBinaryCache :: Maybe Config -> Api.BinaryCache -> UseOptions -> InstallationMode -> IO ()-addBinaryCache _ _ _ UntrustedRequiresSudo =+addBinaryCache _ _ _ UntrustedNixOS = do+  user <- getUser   throwIO $-    MustBeRoot "Run command as root OR execute: $ echo \"trusted-users = root $USER\" | sudo tee -a /etc/nix/nix.conf && sudo pkill nix-daemon"+    MustBeRoot+      [i|This user doesn't have permissions to configure binary caches.++You can either:++a) Run the same command as root to write NixOS configuration.++b) Add the following to your configuration.nix to add your user as trusted +   and then try again:++  trustedUsers = [ "root" "${user}" ];++    |]+addBinaryCache _ _ _ UntrustedRequiresSudo = do+  user <- getUser+  throwIO $+    MustBeRoot+      [i|This user doesn't have permissions to configure binary caches.++You can either:++a) Run the same command as root to configure them globally.++b) Run the following command to add your user as trusted +   and then try again:++  echo "trusted-users = root ${user}" | sudo tee -a /etc/nix/nix.conf && sudo pkill nix-daemon+    |] addBinaryCache maybeConfig bc useOptions WriteNixOS =   nixosBinaryCache maybeConfig bc useOptions addBinaryCache maybeConfig bc _ (Install ncl) = do@@ -84,7 +115,7 @@         lnc <- NixConf.read NixConf.Local         return ([gnc, lnc], lnc)   let nixconf = fromMaybe (NixConf.NixConf []) output-  netrcLocMaybe <- forM (guard $ not (isPublic bc)) $ const $ addPrivateBinaryCacheNetRC maybeConfig bc ncl+  netrcLocMaybe <- forM (guard $ not (Api.isPublic bc)) $ const $ addPrivateBinaryCacheNetRC maybeConfig bc ncl   let addNetRCLine :: NixConf.NixConf -> NixConf.NixConf       addNetRCLine = fromMaybe identity $ do         netrcLoc <- netrcLocMaybe :: Maybe FilePath@@ -94,11 +125,12 @@         pure (NixConf.setNetRC $ toS netrcLoc)   NixConf.write ncl $ addNetRCLine $ NixConf.add bc (catMaybes input) nixconf   filename <- NixConf.getFilename ncl-  putStrLn $ "Configured " <> uri bc <> " binary cache in " <> toS filename+  putStrLn $ "Configured " <> Api.uri bc <> " binary cache in " <> toS filename  nixosBinaryCache :: Maybe Config -> Api.BinaryCache -> UseOptions -> IO () nixosBinaryCache maybeConfig bc UseOptions {useNixOSFolder = baseDirectory} = do-  eitherPermissions <- try $ getPermissions (toS baseDirectory) :: IO (Either SomeException Permissions)+  _ <- try $ createDirectoryIfMissing True $ toS toplevel :: IO (Either SomeException ())+  eitherPermissions <- try $ getPermissions (toS toplevel) :: IO (Either SomeException Permissions)   case eitherPermissions of     Left _ -> throwIO $ NixOSInstructions $ noEtcPermissionInstructions $ toS baseDirectory     Right permissions@@ -106,10 +138,9 @@       | otherwise -> throwIO $ NixOSInstructions $ noEtcPermissionInstructions $ toS baseDirectory   where     installFiles = do-      createDirectoryIfMissing True $ toS toplevel       writeFile (toS glueModuleFile) glueModule       writeFile (toS cacheModuleFile) cacheModule-      unless (isPublic bc) $ void $ addPrivateBinaryCacheNetRC maybeConfig bc NixConf.Global+      unless (Api.isPublic bc) $ void $ addPrivateBinaryCacheNetRC maybeConfig bc NixConf.Global       putText instructions     configurationNix :: Text     configurationNix = toS $ toS baseDirectory </> "configuration.nix"@@ -120,7 +151,7 @@     glueModuleFile :: Text     glueModuleFile = toplevel <> ".nix"     cacheModuleFile :: Text-    cacheModuleFile = toplevel <> "/" <> toS (name bc) <> ".nix"+    cacheModuleFile = toplevel <> "/" <> toS (Api.name bc) <> ".nix"     noEtcPermissionInstructions :: Text -> Text     noEtcPermissionInstructions dir =       [iTrim|@@ -132,7 +163,7 @@     instructions =       [iTrim| Cachix configuration written to ${glueModuleFile}.-Binary cache ${name bc} configuration written to ${cacheModuleFile}.+Binary cache ${Api.name bc} configuration written to ${cacheModuleFile}.  To start using cachix add the following to your ${configurationNix}: @@ -164,10 +195,10 @@ {   nix = {     binaryCaches = [-      "${uri bc}"+      "${Api.uri bc}"     ];     binaryCachePublicKeys = [-      ${T.intercalate " " (map (\s -> "\"" <> s <> "\"") (publicSigningKeys bc))}+      ${T.intercalate " " (map (\s -> "\"" <> s <> "\"") (Api.publicSigningKeys bc))}     ];   }; }@@ -190,12 +221,12 @@   user <- getUser   -- to detect single user installations   permissions <- getPermissions "/nix/store"-  let isTrusted = writable permissions || user `elem` users-  when (not (null groups) && not isTrusted) $ do+  let isTrustedU = writable permissions || user `elem` users+  when (not (null groups) && not isTrustedU) $ do     -- TODO: support Nix group syntax     putText "Warn: cachix doesn't yet support checking if user is trusted via groups, so it will recommend sudo"     putStrLn $ "Warn: groups found " <> T.intercalate "," groups-  return isTrusted+  return isTrustedU   where     groups = filter (\u -> T.head u == '@') users 
src/Cachix/Client/NixConf.hs view
@@ -32,7 +32,7 @@   ) where -import Cachix.Api (BinaryCache (..))+import qualified Cachix.Api as Api import Data.Char (isSpace) import Data.List (nub) import qualified Data.Text as T@@ -85,14 +85,14 @@ isTrustedUsers _ = Nothing  -- | Pure version of addIO-add :: BinaryCache -> [NixConf] -> NixConf -> NixConf+add :: Api.BinaryCache -> [NixConf] -> NixConf -> NixConf add bc toRead toWrite =   writeLines isPublicKey (TrustedPublicKeys $ nub publicKeys) $     writeLines isSubstituter (Substituters $ nub substituters) toWrite   where     -- Note: some defaults are always appended since overriding some setttings in nix.conf overrides defaults otherwise-    substituters = (defaultPublicURI : readLines toRead isSubstituter) <> [uri bc]-    publicKeys = (defaultSigningKey : readLines toRead isPublicKey) <> publicSigningKeys bc+    substituters = (defaultPublicURI : readLines toRead isSubstituter) <> [Api.uri bc]+    publicKeys = (defaultSigningKey : readLines toRead isPublicKey) <> Api.publicSigningKeys bc  defaultPublicURI :: Text defaultPublicURI = "https://cache.nixos.org"
src/Cachix/Client/OptionsParser.hs view
@@ -11,7 +11,6 @@ import qualified Cachix.Client.Config as Config import qualified Cachix.Client.InstallationMode as InstallationMode import Cachix.Client.URI (defaultCachixURI)-import Data.Bifunctor (first) import Options.Applicative import Protolude hiding (option) import URI.ByteString@@ -78,7 +77,8 @@ data PushOptions   = PushOptions       { compressionLevel :: Int,-        numJobs :: Int+        numJobs :: Int,+        omitDeriver :: Bool       }   deriving (Show) @@ -119,6 +119,7 @@               <> showDefault               <> value 4           )+        <*> switch (long "omit-deriver" <> help "Do not publish which derivations built the store paths.")     push = (\opts cache f -> Push $ f opts cache) <$> pushOptions <*> nameArg <*> (pushPaths <|> pushWatchStore)     pushPaths =       (\paths opts cache -> PushPaths opts cache paths)
src/Cachix/Client/Push.hs view
@@ -30,7 +30,7 @@ import qualified Control.Concurrent.QSem as QSem import Control.Exception.Safe (MonadMask, throwM) import Control.Monad.Trans.Resource (ResourceT)-import Control.Retry (RetryPolicy, RetryStatus, constantDelay, limitRetries, recoverAll)+import Control.Retry (RetryPolicy, RetryStatus, exponentialBackoff, limitRetries, recoverAll) import Crypto.Sign.Ed25519 import qualified Data.ByteString.Base64 as B64 import Data.Conduit@@ -63,7 +63,8 @@         on401 :: m r,         onError :: ClientError -> m r,         onDone :: m r,-        withXzipCompressor :: forall a. (ConduitM ByteString ByteString (ResourceT IO) () -> m a) -> m a+        withXzipCompressor :: forall a. (ConduitM ByteString ByteString (ResourceT IO) () -> m a) -> m a,+        omitDeriver :: Bool       }  defaultWithXzipCompressor :: forall m a. (ConduitM ByteString ByteString (ResourceT IO) () -> m a) -> m a@@ -142,7 +143,7 @@             .| passthroughHashSinkB16 fileHashRef     let subdomain =           -- TODO: multipart-          if (fromIntegral storePathSize / (1024 * 1024)) > 100+          if (fromIntegral storePathSize / (1024 * 1024) :: Double) > 100             then "api"             else toS name         newClientEnv =@@ -165,7 +166,10 @@       when (narHash /= toS narHashNix) $ throwM $ NarHashMismatch "Nar hash mismatch between nix-store --dump and nix db"       fileHash <- readIORef fileHashRef       fileSize <- readIORef fileSizeRef-      deriver <- toS <$> Store.validPathInfoDeriver pathinfo+      deriver <-+        if omitDeriver cb+          then pure Store.unknownDeriver+          else toS <$> Store.validPathInfoDeriver pathinfo       referencesPathSet <- Store.validPathInfoReferences pathinfo       references <- sort <$> Store.traversePathSet (pure . toS) referencesPathSet       let fp = fingerprint storePath narHash narSize references@@ -180,7 +184,7 @@                 Api.cFileHash = toS fileHash,                 Api.cReferences = fmap (T.drop 11) references,                 Api.cDeriver =-                  if deriver == "unknown-deriver"+                  if deriver == Store.unknownDeriver                     then deriver                     else T.drop 11 deriver,                 Api.cSig = toS $ B64.encode $ unSignature sig@@ -200,7 +204,7 @@   where     defaultRetryPolicy :: RetryPolicy     defaultRetryPolicy =-      constantDelay 50000 <> limitRetries 3+      exponentialBackoff 100000 <> limitRetries 3  -- | Push an entire closure --
src/Cachix/Client/Store.hs view
@@ -15,6 +15,7 @@     validPathInfoNarSize,     validPathInfoNarHash,     validPathInfoDeriver,+    unknownDeriver,     validPathInfoReferences,      -- * Get closures@@ -134,6 +135,8 @@       |]  -- | Deriver field of a ValidPathInfo struct. Source: store-api.hh+--+-- Returns 'unknownDeriver' when missing. validPathInfoDeriver :: ForeignPtr (Ref ValidPathInfo) -> IO ByteString validPathInfoDeriver vpi =   unsafePackMallocCString@@ -143,6 +146,11 @@           return strdup((deriver == "" ? "unknown-deriver" : deriver->c_str()));         }       |]++-- | String constant representing the case when the deriver of a store path does+-- not exist or is not known. Value: @unknown-deriver@+unknownDeriver :: Text+unknownDeriver = "unknown-deriver"  -- | References field of a ValidPathInfo struct. Source: store-api.hh validPathInfoReferences :: ForeignPtr (Ref ValidPathInfo) -> IO PathSet
test/InstallationModeSpec.hs view
@@ -15,22 +15,22 @@               isNixOS = True             }      in getInstallationMode nixenv `shouldBe` WriteNixOS-  it "NixOS without trust prints configuration plus trust" $+  it "NixOS without trust prints steps to follow" $     let nixenv =           NixEnv             { isTrusted = False,               isRoot = False,               isNixOS = True             }-     in getInstallationMode nixenv `shouldBe` UntrustedRequiresSudo-  it "NixOS without trust prints configuration on older Nix" $+     in getInstallationMode nixenv `shouldBe` UntrustedNixOS+  it "NixOS without trust prints steps to follow" $     let nixenv =           NixEnv             { isTrusted = False,               isRoot = False,               isNixOS = True             }-     in getInstallationMode nixenv `shouldBe` UntrustedRequiresSudo+     in getInstallationMode nixenv `shouldBe` UntrustedNixOS   it "NixOS non-root trusted results into local install" $     let nixenv =           NixEnv
test/NetRcSpec.hs view
@@ -37,11 +37,11 @@  -- TODO: poor man's golden tests, use https://github.com/stackbuilders/hspec-golden test :: [BinaryCache] -> Text -> Expectation-test binaryCaches goldenName = withSystemTempFile "hspec-netrc" $ \filepath _ -> do+test caches goldenName = withSystemTempFile "hspec-netrc" $ \filepath _ -> do   let input = "test/data/" <> toS goldenName <> ".input"       output = "test/data/" <> toS goldenName <> ".output"   copyFile input filepath-  NetRc.add config binaryCaches filepath+  NetRc.add config caches filepath   real <- readFile filepath   expected <- readFile output   real `shouldBe` expected
test/NixVersionSpec.hs view
@@ -15,11 +15,8 @@   it "parses 'nix-env (Nix) 1.11.13' as Nix1XX" $     parseNixVersion "nix-env (Nix) 1.11.13"       `shouldBe` Left "Nix 2.0.2 or lower is not supported. Please upgrade: https://nixos.org/nix/"-  it "parses 'nix-env (Nix) 2.0.5' as Nix201" $-    parseNixVersion "nix-env (Nix) 2.0.5"-      `shouldBe` Right ()-  it "parses 'nix-env (Nix) 2.0.1pre6053_444b921' as Nix201" $-    parseNixVersion "nix-env (Nix) 2.0.1pre6053_444b921"+  it "parses 'nix-env (Nix) 2.0.2' as Nix202" $+    parseNixVersion "nix-env (Nix) 2.0.2"       `shouldBe` Right ()   it "fails with unknown string 'foobar'" $     parseNixVersion "foobar"