packages feed

funflow-nix (empty) → 0.1.0.0

raw patch · 9 files changed

+380/−0 lines, 9 filesdep +asyncdep +basedep +containerssetup-changed

Dependencies added: async, base, containers, data-default, directory, filepath, funflow, funflow-nix, modern-uri, path, path-io, process, random, safe-exceptions, tasty, tasty-hunit, temporary, text, unix

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for funflow-nix++## 0.1.0.0 -- 2018-11-16++* Initial Release
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018, Matthew Pickering++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Matthew Pickering nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,36 @@+`funflow-nix` provides functions for creating flows which run in a nix+environment.++The library exposes the `NixConfig` data type which allows you to specify the+environment and command to run. This is then turned into a flow using `nix`.++A complete example can be seen in `examples/Simple.hs`.++We can pin the version of nixpkgs we want to use by specifying a tarball to+use as the source.++```+tarballSource :: NixpkgsSource+tarballSource = NixpkgsTarball [uri|https://github.com/NixOS/nixpkgs/archive/a19357241973538212b5cb435dde84ad25cbe337.tar.gz|]++nixConfig :: Environment -> NixConfig+nixConfig senv =+  NixShellConfig {+    environment = senv+    , command = "jq"+    , args = [ParamText "--version"]+    , env = []+    , stdout = StdOutCapture+    , nixpkgsSource = tarballSource+  }+```++Once the config has been specified. It can be turned into a flow by using the+`nix` function.+++```+jqVersionPkg :: SimpleFlow () String+jqVersionPkg = readString_ <<< nix (\() -> nixConfig (PackageList ["jq"]))+```+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ examples/Simple.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE Arrows            #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes       #-}+module Main(main) where++import           Control.Arrow+import           Control.Funflow+import           Control.Funflow.ContentHashable+import           Path+import           Path.IO+import           Control.Funflow.External.Nix+import           Text.URI.QQ++nixConfig :: (NixpkgsSource, Environment) -> NixConfig+nixConfig (nps, senv) =+  NixShellConfig {+    environment = senv+    , command = "jq"+    , args = [ParamText "--version"]+    , env = []+    , stdout = StdOutCapture+    , nixpkgsSource = nps+  }++jqVersion :: SimpleFlow NixpkgsSource String+jqVersion = proc np -> do+  cwd <- stepIO (const getCurrentDir) -< ()+  shellScript <- copyFileToStore+                    -< (FileContent (cwd </> [relfile|data/shell.nix|])+                                    ,[relfile|data/shell.nix|])+  readString_ <<< nix nixConfig -< (np, ShellFile shellScript)++jqVersionPkg :: SimpleFlow NixpkgsSource String+jqVersionPkg = readString_ <<< nix (\np -> nixConfig (np, PackageList ["jq"]))++tarballSource :: NixpkgsSource+tarballSource = NixpkgsTarball [uri|https://github.com/NixOS/nixpkgs/archive/a19357241973538212b5cb435dde84ad25cbe337.tar.gz|]++mainFlow :: SimpleFlow () (String, String)+mainFlow = proc _ -> do+  s1 <- jqVersion    -< tarballSource+  s2 <- jqVersionPkg -< NIX_PATH+  returnA -< (s1, s2)++main :: IO ()+main = do+    cwd <- getCurrentDir+    r <- withSimpleLocalRunner (cwd </> [reldir|funflow-example/store|]) $ \run ->+      run mainFlow ()+    case r of+      Left err ->+        putStrLn $ "FAILED" ++ show err+      Right out -> do+        putStrLn $ "SUCCESS"+        print out+
+ funflow-nix.cabal view
@@ -0,0 +1,75 @@+-- Initial funflow-nix.cabal generated by cabal init.  For further+-- documentation, see http://haskell.org/cabal/users-guide/++name:                funflow-nix+version:             0.1.0.0+synopsis:  Utility functions for using funflow with nix+description: This library provides functions to create flows which run commands+             in environments created by nix commands.+             It is designed to be like the docker integration but the environments+             are created by nix rather than in a container.+license:             BSD3+license-file:        LICENSE+author:              Matthew Pickering+maintainer:          matthewtpickering@gmail.com+-- copyright:+-- category:+build-type:          Simple+extra-source-files:  ChangeLog.md, README.md+cabal-version:       >=1.10+tested-with:         GHC==8.2.2++source-repository head+  type:     git+  location: https://github.com/mpickering/funflow-nix.git++flag example+  Description: Build the example executable+  Default:     False+  Manual:      True++library+  exposed-modules: Control.Funflow.External.Nix+  -- other-modules:+  -- other-extensions:+  build-depends:       base >=4.10 && < 5, funflow >= 1.4, text, path, modern-uri+  hs-source-dirs:      src+  ghc-options:         -Wall+  default-language:    Haskell2010+++executable example+  if flag(example)+    buildable: True+  else+    buildable: False+  main-is:           examples/Simple.hs+  build-depends:     funflow, funflow-nix, path, path-io, modern-uri, base < 5+  ghc-options:       -Wall+  default-language:  Haskell2010++Test-suite unit-tests+  type:               exitcode-stdio-1.0+  default-language:   Haskell2010+  hs-source-dirs:     tests+  main-is:            Test.hs+  other-modules:      TestFlows+  ghc-options:        -Wall -threaded+  build-depends:      base < 5+                    , async+                    , containers+                    , data-default >= 0.7+                    , directory+                    , filepath+                    , funflow+                    , funflow-nix+                    , path+                    , path-io+                    , process+                    , random+                    , safe-exceptions+                    , tasty+                    , tasty-hunit+                    , temporary+                    , unix+                    , modern-uri
+ src/Control/Funflow/External/Nix.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DeriveGeneric #-}+module Control.Funflow.External.Nix+  ( NixConfig(..)+  , NixpkgsSource(..)+  , Environment(..)+  , nix )where++import           Control.Funflow+import           Path+import           Control.Funflow.ContentHashable+import           Control.Funflow.ContentStore as CS+import           Control.Funflow.External     as E+import           Data.Semigroup                   ((<>))+import qualified Data.Text                        as T+import           GHC.Generics                     (Generic)+import           Data.List+import           Text.URI (URI)+import qualified Text.URI as URI++data NixpkgsSource = NIX_PATH -- ^ Inherit the @NIX_PATH@ from the environment+                   | NixpkgsTarball URI  -- ^ The 'URI' pointing to a nixpkgs tarball+                   deriving Generic+++data NixConfig =+  NixShellConfig {+    environment :: Environment, -- ^ Specification of the nix environment+    nixpkgsSource :: NixpkgsSource, -- ^ Which version of nixpkgs to use+    command :: T.Text, -- ^ The command to run in the environment+    args :: [ParamField], -- ^ Arguments to pass to the command+    env :: [(T.Text, Param)], -- ^ Environmental variables which are set in the environment+    stdout :: OutputCapture+    }++data Environment = ShellFile (Content File) -- ^ Path to a shell.nix file+                 | PackageList [T.Text] -- ^ A list of packages that+                                        -- will be passed by @-p@.+                 deriving Generic++instance ContentHashable IO Environment where++instance ContentHashable IO NixpkgsSource where+  contentHashUpdate ctx NIX_PATH           = contentHashUpdate_fingerprint ctx NIX_PATH+  contentHashUpdate ctx (NixpkgsTarball s) = contentHashUpdate ctx (URI.render s)++nixpkgsSourceToParam :: NixpkgsSource -> Param+nixpkgsSourceToParam NIX_PATH = envParam "NIX_PATH"+nixpkgsSourceToParam (NixpkgsTarball uri) = textParam ("nixpkgs=" <> URI.render uri)++toExternal :: NixConfig -> ExternalTask+toExternal NixShellConfig{..} = ExternalTask+  { _etCommand = "nix-shell"+  , _etParams =+      [ "--run"+      , Param [ParamCmd (Param (intersperse (ParamText " ") (ParamText command : args)))]+      ] ++ packageSpec environment+  , _etEnv = E.EnvExplicit $ ("NIX_PATH", nixpkgsSourceToParam nixpkgsSource ) : env+  , _etWriteToStdOut = stdout+  }+  where+    packageSpec :: Environment -> [Param]+    packageSpec (ShellFile ip) = [contentParam ip]+    packageSpec (PackageList ps) = [textParam ("-p " <> p) | p <- ps]+++-- | Constructor a flow from a @NixConfig@+nix :: (ContentHashable IO a, ArrowFlow eff ex arr) => (a -> NixConfig)+                                                    -> arr a CS.Item+nix f = external $ toExternal . f
+ tests/Test.hs view
@@ -0,0 +1,10 @@+import qualified TestFlows+import           Test.Tasty++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Unit Tests"+  [ TestFlows.tests+  ]
+ tests/TestFlows.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE Arrows            #-}+{-# LANGUAGE GADTs             #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes       #-}+{-# LANGUAGE TemplateHaskell   #-}+{-# LANGUAGE TypeApplications  #-}++module TestFlows where++import           Control.Arrow+import           Control.Concurrent.Async                    (withAsync)+import           Control.Exception.Safe                      hiding (catch)+import           Control.Funflow+import           Control.Funflow.ContentHashable+import qualified Control.Funflow.ContentStore                as CS+import           Control.Funflow.External.Coordinator.Memory+import           Control.Funflow.External.Executor           (executeLoop)+import           Path+import           Path.IO+import           Test.Tasty+import           Test.Tasty.HUnit+import           Control.Funflow.External.Nix+import           Text.URI.QQ++data FlowAssertion where+  FlowAssertion :: (Eq b, Show b)+                => String -- test name+                -> a  -- input+                -> SimpleFlow a b -- the flow to test+                -> Maybe b --expected output - Nothing for expected failure+                -> IO () -- test setup action+                -> FlowAssertion++mkError :: String -> SomeException+mkError = toException . userError++nixConfig :: (NixpkgsSource, Environment) -> NixConfig+nixConfig (nps, senv) =+  NixShellConfig {+    environment = senv+    , command = "jq"+    , args = [ParamText "--version"]+    , env = []+    , stdout = StdOutCapture+    , nixpkgsSource = nps+  }++-- | Test that we can merge directories within the content store.+jqVersion :: SimpleFlow NixpkgsSource String+jqVersion = proc np -> do+  cwd <- stepIO (const getCurrentDir) -< ()+  shellScript <- copyFileToStore+                    -< (FileContent (cwd </> [relfile|data/shell.nix|]),[relfile|data/shell.nix|])+  readString_ <<< nix nixConfig -< (np, ShellFile shellScript)++jqVersionPkg :: SimpleFlow NixpkgsSource String+jqVersionPkg = readString_ <<< nix (\np -> nixConfig (np, PackageList ["jq"]))++tarballSource :: NixpkgsSource+tarballSource = NixpkgsTarball [uri|https://github.com/NixOS/nixpkgs/archive/a19357241973538212b5cb435dde84ad25cbe337.tar.gz|]+++flowAssertions :: [FlowAssertion]+flowAssertions =+  [+    FlowAssertion "shell 1" NIX_PATH jqVersion (Just "jq-1.6\n") (return ())+  , FlowAssertion "shell 2" NIX_PATH jqVersionPkg (Just "jq-1.6\n") (return ())++  , FlowAssertion "shell tarball 1" tarballSource jqVersion (Just "jq-1.5\n") (return ())+  , FlowAssertion "shell tarball 2" tarballSource jqVersionPkg (Just "jq-1.5\n") (return ())+  ]++testFlowAssertion :: FlowAssertion -> TestTree+testFlowAssertion (FlowAssertion nm x flw expect before) =+  testCase nm $+    withSystemTempDir "test_output_" $ \storeDir ->+    CS.withStore storeDir $ \store -> do+      hook <- createMemoryCoordinator+      before+      res <- withAsync (executeLoop MemoryCoordinator hook store) $ \_ ->+        runSimpleFlow MemoryCoordinator hook store flw x+      assertFlowResult expect res++assertFlowResult :: (Eq a, Show ex, Show a) => Maybe a -> Either ex a -> Assertion+assertFlowResult expect res =+    case (expect, res) of+      (Nothing, Left _) -> return ()+      (Just xr, Right y) -> assertEqual "flow results" xr y+      (Nothing, Right y) -> assertFailure $ "expected flow failure, got success" ++ show y+      (Just xr, Left err) -> assertFailure $ "expected success "++ show xr++", got error" ++ show err++tests :: TestTree+tests = testGroup "Flow Assertions" $ map testFlowAssertion flowAssertions