Add --show-trace, eval unit test

This commit is contained in:
Robert Hensing 2019-07-29 13:49:26 +02:00
parent 381c3ec37f
commit 6d6361e7e8
10 changed files with 182 additions and 30 deletions

View file

@ -27,6 +27,7 @@ data-dir: src
common deps
build-depends: base ^>=4.12.0.0
, aeson
, aeson-pretty
, async
, bytestring
, process
@ -41,6 +42,7 @@ flag ghci
library
import: deps
exposed-modules: Arion.Nix
Arion.Aeson
other-modules: Paths_arion_compose
-- other-extensions:
hs-source-dirs: src/haskell/lib
@ -52,7 +54,6 @@ executable arion
-- other-modules:
-- other-extensions:
build-depends: optparse-applicative
, aeson-pretty
, arion-compose
hs-source-dirs: src/haskell/exe
default-language: Haskell2010
@ -64,7 +65,8 @@ test-suite arion-unit-tests
ghc-options: -Wno-missing-home-modules
type: exitcode-stdio-1.0
main-is: TestMain.hs
-- other-modules:
other-modules: Spec
, Arion.NixSpec
-- other-extensions:
build-depends: arion-compose
, hspec

View file

@ -1,3 +1,13 @@
self: super: hself: hsuper: {
arion-compose = hself.callCabal2nix "arion-compose" ./.. {};
self: super: hself: hsuper:
let
inherit (self.haskell.lib) addBuildTools overrideCabal;
in
{
arion-compose = overrideCabal (addBuildTools (hself.callCabal2nix "arion-compose" ./.. {}) [self.nix]) (o: o // {
preCheck = ''
export NIX_LOG_DIR=$TMPDIR
export NIX_STATE_DIR=$TMPDIR
export NIX_PATH=nixpkgs=${self.path}
'';
});
}

View file

@ -6,6 +6,7 @@
import Protolude hiding (Down)
import Arion.Nix
import Arion.Aeson
import Options.Applicative
import Control.Applicative
@ -26,6 +27,7 @@ data CommonOptions =
CommonOptions
{ files :: NonEmpty FilePath
, pkgs :: Text
, nixArgs :: [Text]
}
deriving (Show)
@ -54,7 +56,15 @@ parseOptions = do
<> value "./arion-pkgs.nix"
<> help "Use Nix expression EXPR to get the Nixpkgs attrset used for bootstrapping \
\and evaluating the configuration." )
pure CommonOptions{..}
showTrace <- flag False True (long "show-trace"
<> help "Causes Nix to print out a stack trace in case of Nix expression evaluation errors.")
-- TODO --option support (https://github.com/pcapriotti/optparse-applicative/issues/284)
userNixArgs <- many (T.pack <$> strOption (long "nix-arg" <> metavar "ARG" <> help "Pass an extra argument to nix. Example: --nix-arg --option --nix-arg substitute --nix-arg false"))
pure $
let nixArgs = userNixArgs <|> "--show-trace" <$ guard showTrace
in CommonOptions{..}
textArgument = fmap T.pack . strArgument
parseCommand :: Parser (CommonOptions -> IO ())
parseCommand =
@ -135,14 +145,16 @@ runEvalAndDC cmd dopts opts = do
runDC cmd dopts opts
runCat :: CommonOptions -> IO ()
runCat (CommonOptions files pkgs) = do
runCat co = do
v <- Arion.Nix.evaluate EvaluationArgs
{ evalUid = 0 -- TODO
, evalModules = files
, evalPkgs = pkgs
, evalModules = files co
, evalPkgs = pkgs co
, evalWorkDir = Nothing
, evalMode = ReadWrite
, evalUserArgs = nixArgs co
}
T.hPutStrLn stderr (TL.toStrict $ TB.toLazyText $ Data.Aeson.Encode.Pretty.encodePrettyToTextBuilder v)
T.hPutStrLn stderr (pretty v)
runRepl :: CommonOptions -> IO ()
runRepl opts =

View file

@ -0,0 +1,20 @@
module Arion.Aeson where
import Data.Aeson
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.IO as TL
import qualified Data.Text.Lazy.Builder as TB
import qualified Data.Aeson.Encode.Pretty
import Data.Aeson.Encode.Pretty ( defConfig
, keyOrder
, confCompare
, confTrailingNewline
)
import Protolude
pretty :: ToJSON a => a -> Text
pretty =
TL.toStrict
. TB.toLazyText
. Data.Aeson.Encode.Pretty.encodePrettyToTextBuilder' config
where config = defConfig { confCompare = compare, confTrailingNewline = True }

View file

@ -21,24 +21,30 @@ import Data.List.NonEmpty ( NonEmpty(..) )
import Control.Arrow ( (>>>) )
data EvaluationMode =
ReadWrite | ReadOnly
data EvaluationArgs = EvaluationArgs
{ evalUid :: Int
, evalModules :: NonEmpty FilePath
, evalPkgs :: Text
, evalWorkDir :: Maybe FilePath
, evalMode :: EvaluationMode
, evalUserArgs :: [Text]
}
evaluate :: EvaluationArgs -> IO Value
evaluate ea = do
evalComposition <- getDataFileName "nix/eval-composition.nix"
let args =
[ evalComposition
, "--eval"
let commandArgs =
[ "--eval"
, "--strict"
, "--read-write-mode"
, "--json"
, "--show-trace"
, "--argstr"
, "--attr"
, "config.build.dockerComposeYamlAttrs"
]
argArgs =
[ "--argstr"
, "uid"
, show $ evalUid ea
, "--arg"
@ -47,9 +53,13 @@ evaluate ea = do
, "--arg"
, "pkgs"
, toS $ evalPkgs ea
, "--attr"
, "config.build.dockerComposeYamlAttrs"
]
args =
[ evalComposition ]
++ commandArgs
++ modeArguments (evalMode ea)
++ argArgs
++ map toS (evalUserArgs ea)
stdin = mempty
procSpec = (proc "nix-instantiate" args) { cwd = evalWorkDir ea }
@ -73,6 +83,9 @@ evaluate ea = do
Right r -> pure r
Left e -> throwIO $ FatalError "Couldn't parse nix-instantiate output"
modeArguments :: EvaluationMode -> [[Char]]
modeArguments ReadWrite = [ "--read-write-mode" ]
modeArguments ReadOnly = [ "--readonly-mode" ]
modulesNixExpr :: NonEmpty FilePath -> [Char]
modulesNixExpr =

View file

@ -1,11 +0,0 @@
module Arion.FooSpec
( spec
)
where
import Test.Hspec
import Test.QuickCheck
spec :: Spec
spec = do
it "foo" $ property True

View file

@ -0,0 +1,49 @@
{-# LANGUAGE OverloadedStrings #-}
module Arion.NixSpec
( spec
)
where
import Protolude
import Test.Hspec
import Test.QuickCheck
import qualified Data.List.NonEmpty as NEL
import Arion.Aeson
import Arion.Nix
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.Text.Lazy.IO as TL
import qualified Data.Text.Lazy.Builder as TB
import qualified Data.Aeson.Encode.Pretty
import Data.Char (isSpace)
spec :: Spec
spec = describe "evaluate" $ it "matches an example" $ do
x <- Arion.Nix.evaluate EvaluationArgs
{ evalUid = 123
, evalModules = NEL.fromList
["src/haskell/testdata/Arion/NixSpec/arion-compose.nix"]
, evalPkgs = "import <nixpkgs> {}"
, evalWorkDir = Nothing
, evalMode = ReadOnly
, evalUserArgs = ["--show-trace"]
}
let actual = pretty x
expected <- T.readFile "src/haskell/testdata/Arion/NixSpec/arion-compose.json"
censorPaths actual `shouldBe` censorPaths expected
censorPaths :: Text -> Text
censorPaths x = case T.breakOn "/nix/store/" x of
(prefix, tl) | (tl :: Text) == "" -> prefix
(prefix, tl) -> prefix <> "<STOREPATH>" <> censorPaths
(T.dropWhile isNixNameChar $ T.drop (T.length "/nix/store/") tl)
-- | WARNING: THIS IS LIKELY WRONG: DON'T REUSE
isNixNameChar :: Char -> Bool
isNixNameChar c | c >= '0' && c <= '9' = True
isNixNameChar c | c >= 'a' && c <= 'z' = True
isNixNameChar c | c >= 'A' && c <= 'Z' = True
isNixNameChar c | c == '-' = True
isNixNameChar c | c == '.' = True
isNixNameChar c | c == '_' = True -- WRONG?
isNixNameChar c = False -- WRONG?

View file

@ -4,8 +4,8 @@ module Spec
where
import Test.Hspec
import qualified Arion.FooSpec
import qualified Arion.NixSpec
spec :: Spec
spec = do
describe "Arion.Foo" Arion.FooSpec.spec
describe "Arion.Nix" Arion.NixSpec.spec

View file

@ -0,0 +1,45 @@
{
"services": {
"webserver": {
"build": {
"context": "/nix/store/l6jwin74n93d66ralxzb001c22yjii9x-arion-image"
},
"command": [
"/nix/store/b9w61w4g8sqgrm3rid6ca22krslqghb3-nixos-system-unnamed-19.03.173100.e726e8291b2/init"
],
"environment": {
"NIX_REMOTE": "",
"container": "docker"
},
"image": "arion-base",
"ports": [
"8000:80"
],
"stop_signal": "SIGRTMIN+3",
"sysctls": {},
"tmpfs": [
"/run",
"/run/wrappers",
"/tmp:exec,mode=777"
],
"tty": true,
"volumes": [
"/sys/fs/cgroup:/sys/fs/cgroup:ro",
"/nix/store:/nix/store",
"/nix/store/pssdmhzjnhflawv7rwk1yw39350iv40g-container-system-env:/run/system"
]
}
},
"version": "3.4",
"x-arion": {
"images": [],
"serviceInfo": {
"webserver": {
"defaultExec": [
"/run/current-system/sw/bin/bash",
"-l"
]
}
}
}
}

View file

@ -0,0 +1,12 @@
{
docker-compose.services.webserver = { pkgs, ... }: {
nixos.useSystemd = true;
nixos.configuration.boot.tmpOnTmpfs = true;
nixos.configuration.services.nginx.enable = true;
nixos.configuration.services.nginx.virtualHosts.localhost.root = "${pkgs.nix.doc}/share/doc/nix/manual";
service.useHostStore = true;
service.ports = [
"8000:80" # host:container
];
};
}