packages feed

cachix 0.1.0.2 → 0.1.1

raw patch · 9 files changed

+128/−111 lines, 9 filesdep −conduit-combinatorsdep ~conduitPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: conduit-combinators

Dependency ranges changed: conduit

API changes (from Hackage documentation)

- Cachix.Client.InstallationMode: UnsupportedNix1X :: InstallationMode
- Cachix.Client.InstallationMode: [nixMode] :: NixEnv -> NixMode
- Cachix.Client.NixConf: BinaryCachePublicKeys :: [Text] -> NixConfLine
- Cachix.Client.NixConf: BinaryCaches :: [Text] -> NixConfLine
- Cachix.Client.NixVersion: data NixMode
- Cachix.Client.NixVersion: getNixMode :: IO (Either Text NixMode)
- Cachix.Client.NixVersion: instance GHC.Classes.Eq Cachix.Client.NixVersion.NixMode
- Cachix.Client.NixVersion: instance GHC.Show.Show Cachix.Client.NixVersion.NixMode
- Cachix.Client.NixVersion: parseNixMode :: Text -> Either Text NixMode
+ Cachix.Client.InstallationMode: [nixVersion] :: NixEnv -> NixVersion
+ Cachix.Client.NixConf: substitutersKey :: NixVersion -> Text
+ Cachix.Client.NixConf: trustedPublicKeysKey :: NixVersion -> Text
+ Cachix.Client.NixVersion: data NixVersion
+ Cachix.Client.NixVersion: getNixVersion :: IO (Either Text NixVersion)
+ Cachix.Client.NixVersion: instance GHC.Classes.Eq Cachix.Client.NixVersion.NixVersion
+ Cachix.Client.NixVersion: instance GHC.Show.Show Cachix.Client.NixVersion.NixVersion
+ Cachix.Client.NixVersion: parseNixVersion :: Text -> Either Text NixVersion
- Cachix.Client.InstallationMode: EchoNixOS :: InstallationMode
+ Cachix.Client.InstallationMode: EchoNixOS :: NixVersion -> InstallationMode
- Cachix.Client.InstallationMode: EchoNixOSWithTrustedUser :: InstallationMode
+ Cachix.Client.InstallationMode: EchoNixOSWithTrustedUser :: NixVersion -> InstallationMode
- Cachix.Client.InstallationMode: Install :: NixConfLoc -> InstallationMode
+ Cachix.Client.InstallationMode: Install :: NixVersion -> NixConfLoc -> InstallationMode
- Cachix.Client.InstallationMode: NixEnv :: NixMode -> Bool -> Bool -> Bool -> NixEnv
+ Cachix.Client.InstallationMode: NixEnv :: NixVersion -> Bool -> Bool -> Bool -> NixEnv
- Cachix.Client.NixConf: render :: NixConf -> Text
+ Cachix.Client.NixConf: render :: NixVersion -> NixConf -> Text
- Cachix.Client.NixConf: update :: NixConfLoc -> (Maybe NixConf -> NixConf) -> IO ()
+ Cachix.Client.NixConf: update :: NixVersion -> NixConfLoc -> (Maybe NixConf -> NixConf) -> IO ()
- Cachix.Client.NixConf: write :: NixConfLoc -> NixConf -> IO ()
+ Cachix.Client.NixConf: write :: NixVersion -> NixConfLoc -> NixConf -> IO ()
- Cachix.Client.NixVersion: Nix1XX :: NixMode
+ Cachix.Client.NixVersion: Nix1XX :: NixVersion
- Cachix.Client.NixVersion: Nix20 :: NixMode
+ Cachix.Client.NixVersion: Nix20 :: NixVersion
- Cachix.Client.NixVersion: Nix201 :: NixMode
+ Cachix.Client.NixVersion: Nix201 :: NixVersion

Files

ChangeLog.md view
@@ -5,7 +5,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased]+## Unreleased++## [0.1.1] - 2018-08-03++### Added++- #102 Nix 1.0 support @domenkozar++### Changed++- #105 Always specify cachix.nixos.org on NixOS @domenkozar+- #98, #95 Use LTS-12.X and Servant 0.14.1 @domenkozar  ## [0.1.0.2] - 2018-07-06 
cachix.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: b62b5e1ccb861524ee1650a2bfa91671a9de1a99b3b66115ef89704165e5b069+-- hash: ab3acbad7328ef095ad03032f8457a46c65d54e21216423686c3a91d85a7b79a  name:           cachix-version:        0.1.0.2+version:        0.1.1 synopsis:       Command line client for Nix binary cache hosting https://cachix.org homepage:       https://github.com/cachix/cachix#readme bug-reports:    https://github.com/cachix/cachix/issues@@ -49,8 +49,7 @@     , bifunctors     , bytestring     , cachix-api-    , conduit-    , conduit-combinators+    , conduit >=1.3.0     , conduit-extra     , cookie     , cryptonite@@ -101,8 +100,7 @@     , bytestring     , cachix     , cachix-api-    , conduit-    , conduit-combinators+    , conduit >=1.3.0     , conduit-extra     , cookie     , cryptonite@@ -159,8 +157,7 @@     , bytestring     , cachix     , cachix-api-    , conduit-    , conduit-combinators+    , conduit >=1.3.0     , conduit-extra     , cookie     , cryptonite
src/Cachix/Client/Commands.hs view
@@ -50,8 +50,8 @@                                                 ) import qualified Cachix.Client.Config          as Config import           Cachix.Client.InstallationMode-import           Cachix.Client.NixVersion       ( getNixMode-                                                , NixMode+import           Cachix.Client.NixVersion       ( getNixVersion+                                                , NixVersion                                                 ) import qualified Cachix.Client.NixConf         as NixConf import           Cachix.Client.Servant@@ -118,23 +118,23 @@     -- TODO: handle 404     Left err -> panic $ show err     Right binaryCache -> do-      eitherNixMode <- getNixMode-      case eitherNixMode of+      eitherNixVersion <- getNixVersion+      case eitherNixVersion of         Left err -> panic err-        Right nixMode -> do+        Right nixVersion -> do           user <- getUser           nc <- NixConf.read NixConf.Global           isTrusted <- isTrustedUser $ NixConf.readLines (catMaybes [nc]) NixConf.isTrustedUsers           isNixOS <- doesFileExist "/etc/NIXOS"           let nixEnv = NixEnv-                { nixMode = nixMode+                { nixVersion = nixVersion                 , isRoot = user == "root"                 , isTrusted = isTrusted                 , isNixOS = isNixOS                 }           addBinaryCache binaryCache $             if shouldEchoNixOS-            then EchoNixOS+            then EchoNixOS nixVersion             else getInstallationMode nixEnv  
src/Cachix/Client/InstallationMode.hs view
@@ -13,7 +13,7 @@ import           Data.String.Here import qualified Data.Text as T import qualified Cachix.Client.NixConf as NixConf-import           Cachix.Client.NixVersion ( NixMode(..) )+import           Cachix.Client.NixVersion ( NixVersion(..) ) import           Cachix.Api as Api import           System.Directory               ( getPermissions, writable ) import           System.Environment             ( lookupEnv )@@ -31,37 +31,33 @@ instance Exception CachixException  data NixEnv = NixEnv-  { nixMode :: NixMode+  { nixVersion :: NixVersion   , isTrusted :: Bool   , isRoot :: Bool   , isNixOS :: Bool   }  data InstallationMode-  = Install NixConf.NixConfLoc-  | UnsupportedNix1X-  | EchoNixOS-  | EchoNixOSWithTrustedUser+  = Install NixVersion NixConf.NixConfLoc+  | EchoNixOS NixVersion+  | EchoNixOSWithTrustedUser NixVersion   | UntrustedRequiresSudo   | Nix20RequiresSudo   deriving (Show, Eq)  getInstallationMode :: NixEnv -> InstallationMode getInstallationMode NixEnv{..}-  | nixMode == Nix1XX = UnsupportedNix1X-  | isNixOS && isRoot = EchoNixOS-  | isNixOS && not isTrusted = EchoNixOSWithTrustedUser-  | not isNixOS && isRoot = Install NixConf.Global-  | nixMode == Nix20 = Nix20RequiresSudo-  | isTrusted = Install NixConf.Local+  | isNixOS && (isRoot || nixVersion /= Nix201) = EchoNixOS nixVersion+  | isNixOS && not isTrusted = EchoNixOSWithTrustedUser nixVersion+  | not isNixOS && isRoot = Install nixVersion NixConf.Global+  | nixVersion /= Nix201 = Nix20RequiresSudo+  | isTrusted = Install nixVersion NixConf.Local   | not isTrusted = UntrustedRequiresSudo   -- | Add a Binary cache to nix.conf, print nixos config or fail addBinaryCache :: Api.BinaryCache -> InstallationMode -> IO ()-addBinaryCache _ UnsupportedNix1X = throwIO $-  UnsupportedNixVersion "Nix 1.x is not supported, please upgrade to Nix 2.0.1 or greater"-addBinaryCache Api.BinaryCache{..} EchoNixOS = do+addBinaryCache Api.BinaryCache{..} (EchoNixOS _) = do   putText [iTrim| nix = {   binaryCaches = [@@ -73,12 +69,13 @@ };   |]   throwIO $ NixOSInstructions "Add above lines to your NixOS configuration file"-addBinaryCache Api.BinaryCache{..} EchoNixOSWithTrustedUser = do+addBinaryCache Api.BinaryCache{..} (EchoNixOSWithTrustedUser _) = do   -- TODO: DRY   user <- getUser   putText [iTrim| nix = {   binaryCaches = [+    "https://cache.nixos.org/"     "${uri}"   ];   binaryCachePublicKeys = [@@ -92,13 +89,13 @@   MustBeRoot "Run command as root OR execute: $ echo \"trusted-users = root $USER\" | sudo tee -a /etc/nix/nix.conf && sudo pkill nix-daemon" addBinaryCache _ Nix20RequiresSudo = throwIO $   MustBeRoot "Run command as root OR upgrade to latest Nix - to be able to use it without root (recommended)"-addBinaryCache bc@Api.BinaryCache{..} (Install ncl) = do+addBinaryCache bc@Api.BinaryCache{..} (Install nixversion ncl) = do   -- TODO: might need locking one day   gnc <- NixConf.read NixConf.Global   lnc <- NixConf.read NixConf.Local   let final = if ncl == NixConf.Global then gnc else lnc       input = if ncl == NixConf.Global then [gnc] else [gnc, lnc]-  NixConf.write ncl $ NixConf.add bc (catMaybes input) (fromMaybe (NixConf.NixConf []) final)+  NixConf.write nixversion ncl $ NixConf.add bc (catMaybes input) (fromMaybe (NixConf.NixConf []) final)   filename <- NixConf.getFilename ncl   putStrLn $ "Configured " <> uri <> " binary cache in " <> toS filename 
src/Cachix/Client/NixConf.hs view
@@ -1,4 +1,11 @@ {- (Very limited) parser, rendered and modifier of nix.conf++Supports subset of nix.conf given Nix 2.0 or Nix 1.0++When reading config files, it normalizes Nix 1.0/2.0 names to unified naming,+then when it writes the config back, it uses naming depending what given Nix+version considers as recommended.+ -} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DeriveFunctor #-}@@ -20,6 +27,8 @@   , isTrustedUsers   , defaultPublicURI   , defaultSigningKey+  , substitutersKey+  , trustedPublicKeysKey   ) where  import Control.Exception (catch)@@ -33,14 +42,13 @@                         , getXdgDirectory, XdgDirectory(..)                         ) import Cachix.Api (BinaryCache(..))+import Cachix.Client.NixVersion (NixVersion(..))   data NixConfLine   = Substituters [Text]   | TrustedUsers [Text]   | TrustedPublicKeys [Text]-  | BinaryCachePublicKeys [Text]-  | BinaryCaches [Text]   | Other Text   deriving (Show, Eq) @@ -64,12 +72,10 @@  isSubstituter :: NixConfLine -> Maybe [Text] isSubstituter (Substituters xs) = Just xs-isSubstituter (BinaryCaches xs) = Just xs isSubstituter _ = Nothing  isPublicKey :: NixConfLine -> Maybe [Text] isPublicKey (TrustedPublicKeys xs) = Just xs-isPublicKey (BinaryCachePublicKeys xs) = Just xs isPublicKey _ = Nothing  isTrustedUsers :: NixConfLine -> Maybe [Text]@@ -91,21 +97,29 @@ defaultSigningKey :: Text defaultSigningKey = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" -render :: NixConf -> Text-render (NixConf nixconflines) = unlines $ fmap go nixconflines+render :: NixVersion -> NixConf -> Text+render nixversion (NixConf nixconflines) = unlines $ fmap go nixconflines   where     go :: NixConfLine -> Text-    go (Substituters xs) = "substituters = " <> unwords xs-    go (BinaryCaches xs) = "substituters = " <> unwords xs+    go (Substituters xs) = substitutersKey nixversion <> " = " <> unwords xs     go (TrustedUsers xs) = "trusted-users = " <> unwords xs-    go (TrustedPublicKeys xs) = "trusted-public-keys = " <> unwords xs-    go (BinaryCachePublicKeys xs) = "trusted-public-keys = " <> unwords xs+    go (TrustedPublicKeys xs) = trustedPublicKeysKey nixversion <> " = " <> unwords xs     go (Other line) = line -write :: NixConfLoc -> NixConf -> IO ()-write ncl nc = do+substitutersKey :: NixVersion -> Text+substitutersKey Nix1XX = "binary-caches"+substitutersKey Nix20 = "substituters"+substitutersKey Nix201 = "substituters"++trustedPublicKeysKey :: NixVersion -> Text+trustedPublicKeysKey Nix1XX = "binary-cache-public-keys"+trustedPublicKeysKey Nix20 = "trusted-public-keys"+trustedPublicKeysKey Nix201 = "trusted-public-keys"++write :: NixVersion -> NixConfLoc -> NixConf -> IO ()+write nixversion ncl nc = do   filename <- getFilename ncl-  writeFile filename $ render nc+  writeFile filename $ render nixversion nc  read :: NixConfLoc -> IO (Maybe NixConf) read ncl = do@@ -121,10 +135,10 @@         panic $ toS filename <> " failed to parse, please copy the above error and contents of nix.conf and open an issue at https://github.com/cachix/cachix"       Right conf -> return $ Just conf -update :: NixConfLoc -> (Maybe NixConf -> NixConf) -> IO ()-update ncl f = do+update :: NixVersion -> NixConfLoc -> (Maybe NixConf -> NixConf) -> IO ()+update nixversion ncl f = do   nc <- f <$> read ncl-  write ncl nc+  write nixversion ncl nc  data NixConfLoc = Global | Local   deriving (Show, Eq)@@ -162,8 +176,8 @@       parseLine Substituters "substituters"   <|> parseLine TrustedPublicKeys "trusted-public-keys"   <|> parseLine TrustedUsers "trusted-users"-  <|> parseLine BinaryCachePublicKeys "binary-cache-public-keys"-  <|> parseLine BinaryCaches "binary-caches"+  <|> parseLine TrustedPublicKeys "binary-cache-public-keys"+  <|> parseLine Substituters "binary-caches"   <|> parseOther  parser :: Parser NixConf
src/Cachix/Client/NixVersion.hs view
@@ -1,7 +1,7 @@ module Cachix.Client.NixVersion-  ( getNixMode-  , parseNixMode-  , NixMode(..)+  ( getNixVersion+  , parseNixVersion+  , NixVersion(..)   ) where  import Protolude@@ -10,22 +10,22 @@ import Data.Text as T  -data NixMode+data NixVersion   = Nix20   | Nix201   | Nix1XX   deriving (Show, Eq) -getNixMode :: IO (Either Text NixMode)-getNixMode = do+getNixVersion :: IO (Either Text NixVersion)+getNixVersion = do   (exitcode, out, err) <- readProcessWithExitCode "nix-env" ["--version"] mempty   unless (err == "") $ putStrLn $ "nix-env stderr: " <> err   return $ case exitcode of     ExitFailure i -> Left $ "'nix-env --version' exited with " <> show i-    ExitSuccess -> parseNixMode $ toS out+    ExitSuccess -> parseNixVersion $ toS out -parseNixMode :: Text -> Either Text NixMode-parseNixMode output =+parseNixVersion :: Text -> Either Text NixVersion+parseNixVersion output =   let     verStr = T.drop 14 $ T.strip output     err = "Couldn't parse 'nix-env --version' output: " <> output
test/InstallationModeSpec.hs view
@@ -4,70 +4,79 @@ import Test.Hspec  import qualified Cachix.Client.NixConf as NixConf-import           Cachix.Client.NixVersion ( NixMode(..) )+import           Cachix.Client.NixVersion ( NixVersion(..) ) import           Cachix.Client.InstallationMode   spec :: Spec spec = describe "getInstallationMode" $ do-  it "Nix 1.X is unsupported" $-    let-      nixenv = NixEnv-        { nixMode = Nix1XX-        , isTrusted = False -- any-        , isRoot = False -- any-        , isNixOS = False -- any-        }-    in getInstallationMode nixenv `shouldBe` UnsupportedNix1X   it "NixOS with root prints configuration" $     let       nixenv = NixEnv-        { nixMode = Nix20 -- any except 1.0+        { nixVersion = Nix20 -- any except 1.0         , isTrusted = True -- any         , isRoot = True         , isNixOS = True         }-    in getInstallationMode nixenv `shouldBe` EchoNixOS+    in getInstallationMode nixenv `shouldBe` EchoNixOS Nix20   it "NixOS without trust prints configuration plus trust" $     let       nixenv = NixEnv-        { nixMode = Nix20+        { nixVersion = Nix201         , isTrusted = False         , isRoot = False         , isNixOS = True         }-    in getInstallationMode nixenv `shouldBe` EchoNixOSWithTrustedUser+    in getInstallationMode nixenv `shouldBe` EchoNixOSWithTrustedUser Nix201+  it "NixOS without trust prints configuration on older Nix" $+    let+      nixenv = NixEnv+        { nixVersion = Nix20+        , isTrusted = False+        , isRoot = False+        , isNixOS = True+        }+    in getInstallationMode nixenv `shouldBe` EchoNixOS Nix20   it "NixOS non-root trusted results into local install" $     let       nixenv = NixEnv-        { nixMode = Nix201+        { nixVersion = Nix201         , isTrusted = True         , isRoot = False         , isNixOS = True         }-    in getInstallationMode nixenv `shouldBe` Install NixConf.Local+    in getInstallationMode nixenv `shouldBe` Install Nix201 NixConf.Local   it "non-NixOS root results into global install" $     let       nixenv = NixEnv-        { nixMode = Nix201+        { nixVersion = Nix201         , isTrusted = True         , isRoot = True         , isNixOS = False         }-    in getInstallationMode nixenv `shouldBe` Install NixConf.Global+    in getInstallationMode nixenv `shouldBe` Install Nix201 NixConf.Global+  it "non-NixOS with Nix 1.X root results into global install" $+      let+        nixenv = NixEnv+          { nixVersion = Nix1XX+          , isTrusted = True+          , isRoot = True+          , isNixOS = False -- any+          }+      in getInstallationMode nixenv `shouldBe` Install Nix1XX NixConf.Global   it "non-NixOS non-root trusted results into local install" $     let       nixenv = NixEnv-        { nixMode = Nix201+        { nixVersion = Nix201         , isTrusted = True         , isRoot = False         , isNixOS = False         }-    in getInstallationMode nixenv `shouldBe` Install NixConf.Local+    in getInstallationMode nixenv `shouldBe` Install Nix201 NixConf.Local   it "non-NixOS non-root non-trusted results into required sudo" $     let       nixenv = NixEnv-        { nixMode = Nix201+        { nixVersion = Nix201         , isTrusted = False         , isRoot = False         , isNixOS = False@@ -76,7 +85,7 @@   it "non-NixOS non-root non-trusted on Nix 2.0.0 results into required sudo with upgrade warning" $     let       nixenv = NixEnv-        { nixMode = Nix20+        { nixVersion = Nix20         , isTrusted = False         , isRoot = False         , isNixOS = False@@ -85,7 +94,7 @@   it "non-NixOS non-root trusted on Nix 2.0.0 results into required sudo with upgrade warning" $     let       nixenv = NixEnv-        { nixMode = Nix20+        { nixVersion = Nix20         , isTrusted = True         , isRoot = False         , isNixOS = False
test/NixConfSpec.hs view
@@ -6,12 +6,16 @@ import Test.Hspec  import Cachix.Client.NixConf as NixConf+import Cachix.Client.NixVersion (NixVersion(..)) import Cachix.Api (BinaryCache(..))   property :: Text -> Expectation-property x = NixConf.render <$> parse x `shouldBe` Right x+property x = NixConf.render Nix20 <$> parse x `shouldBe` Right x +propertyNix1 :: Text -> Expectation+propertyNix1 x = NixConf.render Nix1XX <$> parse x `shouldBe` Right x+ bc = BinaryCache  { name = "name"  , uri = "https://name.cachix.org"@@ -27,6 +31,8 @@       property "substituters = a b c\n"     it "handles all known keys" $       property "substituters = a b c\ntrusted-users = him me\ntrusted-public-keys = a\n"+    it "handles all known keys for Nix 1.0" $+      propertyNix1 "binary-caches = a b c\ntrusted-users = him me\nbinary-cache-public-keys = a\n"     it "random content" $       property "blabla = foobar\nfoo = bar\n"   describe "add" $ do@@ -79,29 +85,12 @@     it "removed duplicates" $       let         globalConf = NixConf-          [ Substituters ["bc1"]-          , BinaryCaches ["bc1"]-          , TrustedPublicKeys ["pub1"]-          , BinaryCachePublicKeys ["pub1"]+          [ Substituters ["bc1", "bc1"]+          , TrustedPublicKeys ["pub1", "pub1"]           ]         localConf = NixConf           [ Substituters ["bc2", "bc2"]           , TrustedPublicKeys ["pub2", "pub2"]-          ]-        result = NixConf-          [ Substituters [defaultPublicURI, "bc1", "bc2", "https://name.cachix.org"]-          , TrustedPublicKeys [defaultSigningKey, "pub1", "pub2", "pub"]-          ]-      in add bc [globalConf, localConf] localConf `shouldBe` result-    it "can mix and match old and new names" $-      let-        globalConf = NixConf-          [ BinaryCaches ["bc1"]-          , BinaryCachePublicKeys ["pub1"]-          ]-        localConf = NixConf-          [ Substituters ["bc2"]-          , TrustedPublicKeys ["pub2"]           ]         result = NixConf           [ Substituters [defaultPublicURI, "bc1", "bc2", "https://name.cachix.org"]
test/NixVersionSpec.hs view
@@ -6,16 +6,16 @@ import Cachix.Client.NixVersion  spec :: Spec-spec = describe "parseNixMode" $ do+spec = describe "parseNixVersion" $ do   it "parses 'nix-env (Nix) 2.0' as Nix20" $-    parseNixMode "nix-env (Nix) 2.0" `shouldBe` Right Nix20+    parseNixVersion "nix-env (Nix) 2.0" `shouldBe` Right Nix20   it "parses 'nix-env (Nix) 2.0.1' as Nix201" $-    parseNixMode "nix-env (Nix) 2.0.1" `shouldBe` Right Nix201+    parseNixVersion "nix-env (Nix) 2.0.1" `shouldBe` Right Nix201   it "parses 'nix-env (Nix) 1.11.13' as Nix1XX" $-    parseNixMode "nix-env (Nix) 1.11.13" `shouldBe` Right Nix1XX+    parseNixVersion "nix-env (Nix) 1.11.13" `shouldBe` Right Nix1XX   it "parses 'nix-env (Nix) 2.0.5' as Nix201" $-    parseNixMode "nix-env (Nix) 2.0.5" `shouldBe` Right Nix201+    parseNixVersion "nix-env (Nix) 2.0.5" `shouldBe` Right Nix201   it "parses 'nix-env (Nix) 2.0.1pre6053_444b921' as Nix201" $-    parseNixMode "nix-env (Nix) 2.0.1pre6053_444b921" `shouldBe` Right Nix201+    parseNixVersion "nix-env (Nix) 2.0.1pre6053_444b921" `shouldBe` Right Nix201   it "fails with unknown string 'foobar'" $-    parseNixMode "foobar" `shouldBe` Left "Couldn't parse 'nix-env --version' output: foobar"+    parseNixVersion "foobar" `shouldBe` Left "Couldn't parse 'nix-env --version' output: foobar"