packages feed

libnix 0.2.0.1 → 0.3.0.0

raw patch · 9 files changed

+169/−113 lines, 9 filesdep +bytestringdep +deepseqdep −protoludedep ~aesondep ~basedep ~errorssetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring, deepseq

Dependencies removed: protolude

Dependency ranges changed: aeson, base, errors

API changes (from Hackage documentation)

- Foreign.Nix.Shellout: data StorePath a
- Foreign.Nix.Shellout: instance GHC.Classes.Eq Foreign.Nix.Shellout.NixExpr
- Foreign.Nix.Shellout.Types: [fromStorePath] :: StorePath a -> FilePath
+ Foreign.Nix.Shellout: NixActionError :: Text -> e -> NixActionError e
+ Foreign.Nix.Shellout: StorePath :: FilePath -> StorePath a
+ Foreign.Nix.Shellout: [actionError] :: NixActionError e -> e
+ Foreign.Nix.Shellout: [actionStderr] :: NixActionError e -> Text
+ Foreign.Nix.Shellout: [unStorePath] :: StorePath a -> FilePath
+ Foreign.Nix.Shellout: data NixActionError e
+ Foreign.Nix.Shellout: newtype StorePath a
+ Foreign.Nix.Shellout.Prefetch: NixActionError :: Text -> e -> NixActionError e
+ Foreign.Nix.Shellout.Prefetch: [actionError] :: NixActionError e -> e
+ Foreign.Nix.Shellout.Prefetch: [actionStderr] :: NixActionError e -> Text
+ Foreign.Nix.Shellout.Prefetch: data NixActionError e
+ Foreign.Nix.Shellout.Types: NixActionError :: Text -> e -> NixActionError e
+ Foreign.Nix.Shellout.Types: [actionError] :: NixActionError e -> e
+ Foreign.Nix.Shellout.Types: [actionStderr] :: NixActionError e -> Text
+ Foreign.Nix.Shellout.Types: [unStorePath] :: StorePath a -> FilePath
+ Foreign.Nix.Shellout.Types: data NixActionError e
+ Foreign.Nix.Shellout.Types: instance GHC.Base.Functor Foreign.Nix.Shellout.Types.NixActionError
+ Foreign.Nix.Shellout.Types: instance GHC.Show.Show e => GHC.Show.Show (Foreign.Nix.Shellout.Types.NixActionError e)
- Foreign.Nix.Shellout: NixAction :: ExceptT (Text, e) IO a -> NixAction e a
+ Foreign.Nix.Shellout: NixAction :: ExceptT (NixActionError e) IO a -> NixAction e a
- Foreign.Nix.Shellout: [unNixAction] :: NixAction e a -> ExceptT (Text, e) IO a
+ Foreign.Nix.Shellout: [unNixAction] :: NixAction e a -> ExceptT (NixActionError e) IO a
- Foreign.Nix.Shellout: runNixAction :: NixAction e a -> IO (Either (Text, e) a)
+ Foreign.Nix.Shellout: runNixAction :: NixAction e a -> IO (Either (NixActionError e) a)
- Foreign.Nix.Shellout.Prefetch: NixAction :: ExceptT (Text, e) IO a -> NixAction e a
+ Foreign.Nix.Shellout.Prefetch: NixAction :: ExceptT (NixActionError e) IO a -> NixAction e a
- Foreign.Nix.Shellout.Prefetch: [unNixAction] :: NixAction e a -> ExceptT (Text, e) IO a
+ Foreign.Nix.Shellout.Prefetch: [unNixAction] :: NixAction e a -> ExceptT (NixActionError e) IO a
- Foreign.Nix.Shellout.Prefetch: runNixAction :: NixAction e a -> IO (Either (Text, e) a)
+ Foreign.Nix.Shellout.Prefetch: runNixAction :: NixAction e a -> IO (Either (NixActionError e) a)
- Foreign.Nix.Shellout.Types: NixAction :: ExceptT (Text, e) IO a -> NixAction e a
+ Foreign.Nix.Shellout.Types: NixAction :: ExceptT (NixActionError e) IO a -> NixAction e a
- Foreign.Nix.Shellout.Types: [unNixAction] :: NixAction e a -> ExceptT (Text, e) IO a
+ Foreign.Nix.Shellout.Types: [unNixAction] :: NixAction e a -> ExceptT (NixActionError e) IO a
- Foreign.Nix.Shellout.Types: runNixAction :: NixAction e a -> IO (Either (Text, e) a)
+ Foreign.Nix.Shellout.Types: runNixAction :: NixAction e a -> IO (Either (NixActionError e) a)

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# libnix++## 0.3.0.0 -- 2021-11-16++* Remove `Eq` instance from `NixExpr` (not sensible)+* Replace the `NixAction` tuple with a more semantic `NixActionError`+* Update to 2021 and remove dependency on protolude++## 0.2.0.0 -- 2018-08-12++* First released version.
Foreign/Nix/Shellout.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns #-} {-| Module      : Foreign.Nix.Shellout Description : Interface to the nix package manager’s CLI@@ -23,26 +26,35 @@ , parseInstRealize , NixError(..)   -- * Types-, StorePath(fromStorePath), Derivation, Realized+, StorePath(..), Derivation, Realized , NixExpr-, runNixAction, NixAction(..)+, runNixAction, NixAction(..), NixActionError(..) ) where -import Protolude hiding (show, isPrefixOf)-import Control.Error hiding (bool, err)-import Data.String (String)-import Data.Text (stripPrefix, lines, isPrefixOf)-import System.FilePath (isValid)-import Text.Show (Show(..))+import Control.Error ( throwE, tryLast )+import Data.Text (stripPrefix, lines, isPrefixOf, Text)  import qualified Foreign.Nix.Shellout.Helpers as Helpers import Foreign.Nix.Shellout.Types+    ( Realized,+      Derivation,+      StorePath(..),+      NixActionError(..),+      NixAction(..),+      runNixAction )+import qualified Data.Text as Text+import System.Exit (ExitCode(ExitFailure, ExitSuccess))+import Data.Bifunctor (bimap, Bifunctor (first))+import Control.Monad ((>=>))+import qualified System.FilePath as FilePath+import Data.Function ((&))+import qualified Data.List as List  ------------------------------------------------------------------------------ -- Parsing  -- | A sucessfully parsed nix expression.-newtype NixExpr = NixExpr Text deriving (Show, Eq)+newtype NixExpr = NixExpr Text deriving (Show)  data ParseError   = SyntaxError Text@@ -84,9 +96,10 @@ -- | Just tests if the expression can be evaluated. -- That doesn’t mean it has to instantiate however. eval :: NixExpr -> NixAction InstantiateError ()-eval (NixExpr e) =-  void $ first parseInstantiateError+eval (NixExpr e) = do+  _instantiateOutput <- first parseInstantiateError        $ evalNixOutput "nix-instantiate" [ "--eval", "-E", e ]+  pure ()  parseInstantiateError :: Text -> InstantiateError parseInstantiateError@@ -104,11 +117,11 @@ -- This will typically take a while so it should be executed asynchronously. realize :: StorePath Derivation -> NixAction RealizeError (StorePath Realized) realize (StorePath d) =-     storeOp [ "-r", toS d ]+     storeOp [ "-r", Text.pack d ]  -- | Copy the given file or folder to the nix store and return it’s path. addToStore :: FilePath -> NixAction RealizeError (StorePath Realized)-addToStore fp = storeOp [ "--add", toS fp ]+addToStore fp = storeOp [ "--add", Text.pack fp ]  storeOp :: [Text] -> NixAction RealizeError (StorePath Realized) storeOp op =@@ -144,18 +157,23 @@               -- ^ error: (stderr, errormsg), success: path evalNixOutput = Helpers.readProcess (\(out, err) -> \case   ExitFailure _ -> throwE $-    case mconcat . intersperse "\n"-      . dropWhile (not . isPrefixOf "error: ")-      . lines $ toS err of+    case+      err+        & Text.lines+        & dropWhile (not . isPrefixOf "error: ")+        & List.intersperse "\n"+        & mconcat of       "" -> "nix didn’t output any error message"       s  -> s   ExitSuccess -> tryLast-      "nix didn’t output a store path" (lines $ toS out))+      "nix didn’t output a store path" (Data.Text.lines out))   -- | Apply filePath p to constructor a if it’s a valid filepath toNixFilePath :: (String -> a) -> Text -> NixAction Text a-toNixFilePath a p@(toS -> ps) = NixAction $-  if isValid ps then (pure $ a ps)-  else (throwE $ (nostderr, p <> " is not a filepath!"))+toNixFilePath a p = NixAction $+  if FilePath.isValid (Text.unpack p) then pure $ a (Text.unpack p)+  else throwE $ NixActionError+          { actionStderr = nostderr+          , actionError = p <> " is not a filepath!" }   where nostderr = mempty
Foreign/Nix/Shellout/Helpers.hs view
@@ -1,6 +1,6 @@+{-# LANGUAGE OverloadedStrings #-} module Foreign.Nix.Shellout.Helpers where -import Protolude hiding (async, wait) import Foreign.Nix.Shellout.Types import qualified System.Process as P import qualified Data.Text.IO as TIO@@ -8,9 +8,20 @@ import qualified System.IO as SIO  -- needed for ignoreSigPipe-import GHC.IO.Exception (IOErrorType(..), IOException(..))+-- needed for ignoreSigPipe+import GHC.IO.Exception (IOErrorType(..), IOException(..), ExitCode) import Foreign.C.Error (Errno(Errno), ePIPE)+import Data.Text (Text)+import Control.Error (ExceptT, withExceptT)+import Control.Concurrent (MVar, newEmptyMVar, forkIO, takeMVar, putMVar, killThread)+import Control.DeepSeq (rnf) +import Control.Exception (SomeException, throwIO, onException, try, mask, handle, evaluate)++import Control.Monad (unless)+import Control.Monad.IO.Class (liftIO)+import qualified Data.Text as Text+ -- | Read the output of a process into a NixAction. -- | Keeps stderr if process returns a failure exit code. -- | The text is decoded as @UTF-8@.@@ -25,8 +36,12 @@ readProcess with exec args = NixAction $ do   (exc, out, err) <- liftIO     $ readCreateProcessWithExitCodeAndEncoding-        (P.proc (toS exec) (map toS args)) SIO.utf8 ""-  withExceptT (err,) $ with (out, err) exc+        (P.proc (Text.unpack exec) (map Text.unpack args)) SIO.utf8 ""+  withExceptT+    (\e -> NixActionError+             { actionStderr = err+             , actionError = e })+    $ with (out, err) exc   -- Copied & modified from System.Process (process-1.6.4.0)@@ -62,7 +77,7 @@            -- now write any input           unless (T.null input) $-            ignoreSigPipe $ hPutStr inh input+            ignoreSigPipe $ TIO.hPutStr inh input           -- hClose performs implicit hFlush, and thus may trigger a SIGPIPE           ignoreSigPipe $ SIO.hClose inh 
Foreign/Nix/Shellout/Prefetch.hs view
@@ -9,6 +9,8 @@ into nice reusable data types. -} {-# LANGUAGE RecordWildCards, GeneralizedNewtypeDeriving, ApplicativeDo #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-} module Foreign.Nix.Shellout.Prefetch ( -- * nix-prefetch-url   url, UrlOptions(..), defaultUrlOptions@@ -18,10 +20,9 @@ , PrefetchError(..) , Url(..), Sha256(..)   -- * Reexports-, runNixAction, NixAction(..)+, runNixAction, NixAction(..), NixActionError(..) ) where -import Protolude import Control.Error hiding (bool, err) import qualified Data.Text as T @@ -30,6 +31,15 @@  import Foreign.Nix.Shellout.Types import qualified Foreign.Nix.Shellout.Helpers as Helpers+import Data.Text (Text)+import Data.String (IsString)+import GHC.IO.Exception (ExitCode(ExitFailure, ExitSuccess))+import qualified Data.Text as Text+import Data.Bool (bool)+import Data.Bifunctor (first)+import qualified Data.Text.Lazy as Text.Lazy+import qualified Data.Text.Lazy.Encoding as Text.Lazy.Encoding+import qualified Data.List as List  data PrefetchError   = PrefetchOutputMalformed Text@@ -69,7 +79,7 @@ url UrlOptions{..} = Helpers.readProcess handler exec args   where     exec = "nix-prefetch-url"-    args =  bool [] ["--unpack"] urlUnpack+    args = if urlUnpack then ["--unpack"] else []          <> maybe [] (\n -> ["--name", n]) urlName          <> [ "--type", "sha256"             , "--print-path"@@ -82,7 +92,7 @@         path <- tryLast (exec <> " didn’t output a store path") ls         sha  <- let errS = (exec <> " didn’t output a hash")                 in tryInit errS ls >>= tryLast errS-        pure (Sha256 sha, StorePath $ toS path)+        pure (Sha256 sha, StorePath $ Text.unpack path)       ExitFailure _ -> throwE $         if "error: hash mismatch" `T.isPrefixOf` err         then ExpectedHashError@@ -137,23 +147,23 @@             -- we need @url [rev [hash]]@,             -- otherwise we can’t expect a hash             , unUrl gitUrl-            , maybe "" identity gitRev ]+            , fromMaybe "" gitRev ]             -- hash comes last          <> maybe [] (\(Sha256 h) -> [h]) gitExpectedHash      handler (out, err) = \case       ExitSuccess -> withExceptT PrefetchOutputMalformed $ do-        let error msg = exec <> " " <> msg+        let error' msg = exec <> " " <> msg             jsonError :: [Char] -> Text-            jsonError = \msg -> error (T.intercalate "\n"+            jsonError = \msg -> error' (T.intercalate "\n"                       [ "parsing json output failed:"-                      , toS msg+                      , Text.pack msg                       , "The output was:"                       , out ])          (gitOutputRev, gitOutputSha256)           <- ExceptT . pure . first jsonError $ do-            val <- Aeson.eitherDecode' (toS out)+            val <- Aeson.eitherDecode' (Text.Lazy.Encoding.encodeUtf8 $ Text.Lazy.fromStrict out)             flip AesonT.parseEither val               $ Aeson.withObject "GitPrefetchOutput" $ \obj -> do                     (,) <$> obj Aeson..: "rev"@@ -162,11 +172,11 @@         -- The path isn’t output in the json, but on stderr. :(         -- So this is a bit more hacky than necessary.         gitOuputPath <- case-          find ("path is /nix/store" `T.isPrefixOf`) (T.lines err)+          List.find ("path is /nix/store" `T.isPrefixOf`) (T.lines err)           >>= T.stripPrefix "path is " of           Nothing -> throwE             $ error "could not find nix store output path on stderr"-          Just path -> pure $ StorePath $ toS path+          Just path -> pure $ StorePath $ Text.unpack path          pure GitOutput{..} 
Foreign/Nix/Shellout/Types.hs view
@@ -4,19 +4,29 @@ License     : GPL-3 Stability   : experimental -}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveFunctor #-} module Foreign.Nix.Shellout.Types where -import Protolude import Control.Error+import Control.Monad.IO.Class (MonadIO)+import Data.Text (Text)+import Data.Bifunctor (Bifunctor (bimap))  -- | Calls a command that returns an error and the whole stderr on failure. newtype NixAction e a = NixAction-  { unNixAction :: ExceptT (Text, e) IO a }+  { unNixAction :: ExceptT (NixActionError e) IO a }   deriving (Functor, Applicative, Monad, MonadIO) +-- | Combines the standard error of running a command with a more semantic+-- error type one should match on first.+data NixActionError e = NixActionError+  { actionStderr :: Text+  , actionError :: e }+  deriving (Show, Functor)+ -- | Run a 'NixAction' without having to go through 'ExceptT' first.-runNixAction :: NixAction e a -> IO (Either (Text, e) a)+runNixAction :: NixAction e a+             -> IO (Either (NixActionError e) a) runNixAction = runExceptT . unNixAction  instance Bifunctor NixAction where@@ -25,7 +35,7 @@ -- | A path in the nix store. It carries a phantom @a@ to differentiate -- between 'Derivation' files and 'Realized' paths. newtype StorePath a = StorePath-  { fromStorePath :: FilePath }+  { unStorePath :: FilePath }   deriving (Eq, Show)  -- | A nix derivation is a complete build instruction that can be realized.
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
libnix.cabal view
@@ -1,5 +1,5 @@ name:                libnix-version:             0.2.0.1+version:             0.3.0.0 synopsis:            Bindings to the nix package manager description:         Use the nix package manager from Haskell. All modules are designed to be imported qualified. category:            Foreign, Nix@@ -12,11 +12,12 @@ build-type:          Simple cabal-version:       >=1.10 -extra-source-files:  +extra-source-files:    LICENSE+   CHANGELOG.md    README.md    shell.nix-   + source-repository head   type: git   location: https://github.com/Profpatsch/libnix-haskell@@ -26,20 +27,16 @@                      , Foreign.Nix.Shellout.Prefetch                      , Foreign.Nix.Shellout.Types   other-modules:       Foreign.Nix.Shellout.Helpers-  build-depends:       base >=4.9 && <5-                     , aeson >=1.0.0.0 && <1.5.0.0-                     , errors >=2.2.0 && <2.4.0+  build-depends:       base >=4.9 && <5.0+                     , aeson >=1.0.0.0+                     , bytestring+                     , deepseq+                     , errors >=2.2.0                      , filepath-                     , protolude ==0.2.*                      , process                      , text-  -- hs-source-dirs:      +  -- hs-source-dirs:   ghc-options:        -Wall-  default-extensions:  NoImplicitPrelude-                     , OverloadedStrings-                     , LambdaCase-                     , TupleSections-                     , ViewPatterns   default-language:    Haskell2010  test-suite tests@@ -52,12 +49,7 @@                      , errors                      , text                      , libnix-                     , protolude                      , tasty                      , tasty-hunit   ghc-options:        -threaded -rtsopts -with-rtsopts=-N -Wall-  default-extensions:  NoImplicitPrelude-                     , OverloadedStrings-                     , LambdaCase-                     , TupleSections   default-language:    Haskell2010
shell.nix view
@@ -1,33 +1,11 @@-((import <nixpkgs> {}).haskellPackages.override {-  overrides = self:-    super:-      {-        my-pkg = let-          buildDepends = with self;-          [-            aeson-            errors-            filepath-            protolude-            tasty-            tasty-hunit-          ];-        in super.mkDerivation {-          pname = "pkg-env";-          src = "/dev/null";-          version = "none";-          license = "none";-          inherit buildDepends;-          buildTools = with self;-          [-            ghcid-            cabal-install-            hpack-            hscolour-            (hoogleLocal {-              packages = buildDepends;-            })-          ];-        };-      };-}).my-pkg.env+{ pkgs ? import <nixpkgs> {}}:++pkgs.haskellPackages.shellFor {+  packages = hps: [+    (hps.callCabal2nix "libnix" (pkgs.nix-gitignore.gitignoreSource [".git"] ./.) {})+  ];+  buildInputs = [+    pkgs.cabal-install+    pkgs.haskell-language-server+  ];+}
tests/TestShellout.hs view
@@ -1,6 +1,7 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-} module TestShellout where -import Protolude hiding (check) import qualified Data.Text as T import System.IO (openTempFile, hClose) import System.Directory (getTemporaryDirectory)@@ -9,6 +10,10 @@ import Test.Tasty.HUnit  import Foreign.Nix.Shellout+import Data.Text (Text)+import Control.Monad.IO.Class (liftIO)+import Data.Bifunctor (first)+import Control.Monad ((>=>))   shelloutTests :: TestTree@@ -30,17 +35,17 @@ nixpkgsExists, multilineErrors, helloWorld, copyTempfileToStore :: TestTree  syntaxError = testCase "syntax error"-  $ parseNixExpr ";"-  `isE` (Left ("", SyntaxError "unexpected ';', at (string):1:1"))+  $ parseNixExpr ";" `isENoFail`+      Left ("", SyntaxError "unexpected ';', at (string):1:1")  infiniteRecursion = testCase "infinite recursion"   $ parseInst "let a = a; in a"-  `isE` (Left ( "error: infinite recursion encountered, at (string):1:10"-              , UnknownInstantiateError))+  `isE` Left ( "error: infinite recursion encountered, at (string):1:10"+              , UnknownInstantiateError)  notADerivation = testCase "not a derivation"   $ parseInst "42"-  `isE` (Left ("", NotADerivation))+  `isE` Left ("", NotADerivation)  someDerivation = testCase "a basic derivation"   $ assertNoFailure $ parseInst@@ -51,10 +56,10 @@  multilineErrors = testCase "nixpkgs multiline stderr it parsed"   $ parseEval "builtins.abort ''wow\nsuch error''"-  `isE` (Left+  `isE` Left            ( "error: evaluation aborted with the following error \              \message: 'wow\nsuch error'"-           , UnknownInstantiateError))+           , UnknownInstantiateError)  helloWorld = testCase "build the GNU hello package"   $ assertNoFailure $ parseInstRealize "with import <nixpkgs> {}; hello"@@ -83,23 +88,42 @@   first (\_ -> UnknownInstantiateError)               . parseNixExpr >=> like -isE :: (Eq e, Eq a, Show a, Show e)+isE :: (Eq a, Eq e, Show a, Show e)     => NixAction e a     -> Either (Text, e) a        -- ^ Left (subset of stdout, error)     -> Assertion-isE na match = runExceptT (unNixAction na) >>= check match-  where-    check (Left (outMatch, err)) (Left (out, err')) = do-      assertBool "stderr not matched" (outMatch `T.isInfixOf` out)-      err @=? err'-    check (Right _) (Left _) =-      assertFailure "output should have succeeded, but is failed"-    check a b = a @=? b+isE na match = runNixAction na >>= \res ->+  case (match, res) of+    (Right a, Right b) -> a @=? b+    (match', res') -> check match' res' +isENoFail :: (Eq e, Show a, Show e)+          => NixAction e a+          -> Either (Text, e) a+            -- ^ Left (subset of stdout, error)+          -> Assertion+isENoFail na match = runNixAction na >>= check match++check :: (Eq a, Show a)+      => Either (Text, a) _x+      -> Either (NixActionError a) _x+      -> Assertion+check (Left (outMatch, err))+      (Left NixActionError+                { actionStderr = out+                , actionError = err' }) = do+  assertBool "stderr not matched" (outMatch `T.isInfixOf` out)+  err @=? err'+check (Right _) (Left _) =+  assertFailure "output should have succeeded, but it failed"+check (Left _) (Right _) =+  assertFailure "output should have failed, but it succeeded"+check _ _ = error "handled by isE"+ assertNoFailure :: Show e => NixAction e a -> Assertion assertNoFailure na = do-  ei <- runExceptT (unNixAction na)+  ei <- runNixAction na   case ei of-    (Left (_, e)) -> assertFailure $ show e+    (Left naErr) -> assertFailure $ show $ actionError naErr     (Right _) -> pure ()