Make arion cat work
This commit is contained in:
parent
77eadf4c41
commit
6882a92e56
11 changed files with 143 additions and 32 deletions
|
@ -3,3 +3,4 @@
|
||||||
## 0.1.0.0 -- YYYY-mm-dd
|
## 0.1.0.0 -- YYYY-mm-dd
|
||||||
|
|
||||||
* First version. Released on an unsuspecting world.
|
* First version. Released on an unsuspecting world.
|
||||||
|
* *BREAKING:* useHostStore now uses a proper empty base image, like `scratch`.
|
||||||
|
|
|
@ -19,6 +19,7 @@ data-files: nix/*.nix
|
||||||
, nix/modules/composition/*.nix
|
, nix/modules/composition/*.nix
|
||||||
, nix/modules/nixos/*.nix
|
, nix/modules/nixos/*.nix
|
||||||
, nix/modules/service/*.nix
|
, nix/modules/service/*.nix
|
||||||
|
, arion-image/Dockerfile
|
||||||
|
|
||||||
-- all data is verbatim from some sources
|
-- all data is verbatim from some sources
|
||||||
data-dir: src
|
data-dir: src
|
||||||
|
@ -26,16 +27,19 @@ data-dir: src
|
||||||
common deps
|
common deps
|
||||||
build-depends: base ^>=4.12.0.0
|
build-depends: base ^>=4.12.0.0
|
||||||
, aeson
|
, aeson
|
||||||
|
, async
|
||||||
|
, bytestring
|
||||||
|
, process
|
||||||
|
, process-extras
|
||||||
, text
|
, text
|
||||||
, protolude
|
, protolude
|
||||||
|
|
||||||
|
|
||||||
library
|
library
|
||||||
import: deps
|
import: deps
|
||||||
-- exposed-modules:
|
exposed-modules: Arion.Nix
|
||||||
-- other-modules:
|
other-modules: Paths_arion_compose
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: process
|
|
||||||
hs-source-dirs: src/haskell/lib
|
hs-source-dirs: src/haskell/lib
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
@ -45,7 +49,8 @@ executable arion
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: optparse-applicative
|
build-depends: optparse-applicative
|
||||||
, process
|
, aeson-pretty
|
||||||
|
, arion-compose
|
||||||
hs-source-dirs: src/haskell/exe
|
hs-source-dirs: src/haskell/exe
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
|
# TODO: Replace by a new expression for the new Haskell main
|
||||||
arion = stdenv.mkDerivation {
|
arion = stdenv.mkDerivation {
|
||||||
name = "arion";
|
name = "arion";
|
||||||
src = ./src;
|
src = ./src;
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
self: super:
|
self: super:
|
||||||
let
|
let
|
||||||
inherit (self.arion-project) haskellPkgs;
|
inherit (self.arion-project) haskellPkgs;
|
||||||
|
|
||||||
|
srcDir = ../src; # TODO gitignoreSource + whitelist nix and arion-image
|
||||||
|
eval = import (srcDir + "/nix/eval-composition.nix");
|
||||||
|
build = args@{...}:
|
||||||
|
let composition = eval args;
|
||||||
|
in composition.config.build.dockerComposeYaml;
|
||||||
|
hlib = super.haskell.lib;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
arion-v0 = super.callPackage ../arion.nix {};
|
arion-v0 = super.callPackage ../arion.nix {};
|
||||||
arion = super.haskell.lib.justStaticExecutables haskellPkgs.arion-compose;
|
arion = hlib.justStaticExecutables (hlib.overrideCabal haskellPkgs.arion-compose (o: {
|
||||||
|
passthru = o.passthru // {
|
||||||
|
inherit eval build;
|
||||||
|
};
|
||||||
|
}));
|
||||||
tests = super.callPackage ../tests {};
|
tests = super.callPackage ../tests {};
|
||||||
doc = super.callPackage ../doc {};
|
doc = super.callPackage ../doc {};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY passwd /etc/passwd
|
# scratch itself can't be run.
|
||||||
ADD tarball.tar.gz /
|
|
||||||
|
# This seems like a no-op:
|
||||||
|
CMD []
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
root:x:0:0:System administrator:/root:/bin/sh
|
|
||||||
nobody:x:65534:65534:Unprivileged account (don't use!):/var/empty:/run/current-system/sw/bin/nologin
|
|
|
@ -1 +0,0 @@
|
||||||
/run/system/bin/sh
|
|
|
@ -1 +0,0 @@
|
||||||
/run/system/usr/bin/env
|
|
|
@ -5,15 +5,21 @@
|
||||||
|
|
||||||
import Protolude hiding (Down)
|
import Protolude hiding (Down)
|
||||||
|
|
||||||
|
import Arion.Nix
|
||||||
|
|
||||||
import Options.Applicative
|
import Options.Applicative
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
|
|
||||||
|
import qualified Data.Aeson.Encode.Pretty
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.IO as T
|
import qualified Data.Text.IO as T
|
||||||
|
import qualified Data.Text.Lazy as TL
|
||||||
|
import qualified Data.Text.Lazy.Builder as TB
|
||||||
|
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
import Data.List.NonEmpty (NonEmpty(..))
|
import Data.List.NonEmpty (NonEmpty(..))
|
||||||
|
|
||||||
|
|
||||||
import Control.Arrow ((>>>))
|
import Control.Arrow ((>>>))
|
||||||
|
|
||||||
data CommonOptions =
|
data CommonOptions =
|
||||||
|
@ -46,7 +52,7 @@ parseOptions = do
|
||||||
<> metavar "EXPR"
|
<> metavar "EXPR"
|
||||||
<> showDefault
|
<> showDefault
|
||||||
<> value "./arion-pkgs.nix"
|
<> value "./arion-pkgs.nix"
|
||||||
<> help "Use EXPR to get the Nixpkgs attrset used for bootstrapping \
|
<> help "Use Nix expression EXPR to get the Nixpkgs attrset used for bootstrapping \
|
||||||
\and evaluating the configuration." )
|
\and evaluating the configuration." )
|
||||||
pure CommonOptions{..}
|
pure CommonOptions{..}
|
||||||
|
|
||||||
|
@ -111,28 +117,12 @@ commandDC run cmdStr help =
|
||||||
(run cmdStr <$> parseDockerComposeArgs)
|
(run cmdStr <$> parseDockerComposeArgs)
|
||||||
(progDesc (T.unpack help) <> fullDesc <> forwardOptions))
|
(progDesc (T.unpack help) <> fullDesc <> forwardOptions))
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
modulesNixExpr :: NonEmpty FilePath -> Text
|
|
||||||
modulesNixExpr =
|
|
||||||
NE.toList
|
|
||||||
>>> fmap pathExpr
|
|
||||||
>>> T.unwords
|
|
||||||
>>> wrapList
|
|
||||||
where
|
|
||||||
pathExpr path | isAbsolute path = "(/. + \"" <> T.pack path <> "\")"
|
|
||||||
| otherwise = "(./. + \"" <> T.pack path <> "\")"
|
|
||||||
|
|
||||||
isAbsolute ('/':_) = True
|
|
||||||
isAbsolute _ = False
|
|
||||||
|
|
||||||
wrapList s = "[ " <> s <> " ]"
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
runDC :: Text -> DockerComposeArgs -> CommonOptions -> IO ()
|
runDC :: Text -> DockerComposeArgs -> CommonOptions -> IO ()
|
||||||
runDC cmd (DockerComposeArgs args) opts =
|
runDC cmd (DockerComposeArgs args) opts =
|
||||||
T.putStrLn $ "TODO: docker-compose " <> cmd <> " " <> T.unwords args
|
panic $ "TODO: docker-compose " <> cmd <> " " <> T.unwords args
|
||||||
|
|
||||||
runBuildAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO ()
|
runBuildAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO ()
|
||||||
runBuildAndDC cmd dopts opts = do
|
runBuildAndDC cmd dopts opts = do
|
||||||
|
@ -146,8 +136,13 @@ runEvalAndDC cmd dopts opts = do
|
||||||
|
|
||||||
runCat :: CommonOptions -> IO ()
|
runCat :: CommonOptions -> IO ()
|
||||||
runCat (CommonOptions files pkgs) = do
|
runCat (CommonOptions files pkgs) = do
|
||||||
T.putStrLn "Running cat ... TODO"
|
v <- Arion.Nix.evaluate EvaluationArgs
|
||||||
T.putStrLn (modulesNixExpr files)
|
{ evalUid = 0 -- TODO
|
||||||
|
, evalModules = files
|
||||||
|
, evalPkgs = pkgs
|
||||||
|
, evalWorkDir = Nothing
|
||||||
|
}
|
||||||
|
T.hPutStrLn stderr (TL.toStrict $ TB.toLazyText $ Data.Aeson.Encode.Pretty.encodePrettyToTextBuilder v)
|
||||||
|
|
||||||
runRepl :: CommonOptions -> IO ()
|
runRepl :: CommonOptions -> IO ()
|
||||||
runRepl opts =
|
runRepl opts =
|
||||||
|
|
91
src/haskell/lib/Arion/Nix.hs
Normal file
91
src/haskell/lib/Arion/Nix.hs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module Arion.Nix where
|
||||||
|
|
||||||
|
import Prelude ( )
|
||||||
|
import Protolude
|
||||||
|
import Data.Aeson
|
||||||
|
import qualified Data.String
|
||||||
|
import System.Process
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
import qualified System.Process.ByteString.Lazy
|
||||||
|
as PBL
|
||||||
|
import Paths_arion_compose
|
||||||
|
import Control.Applicative
|
||||||
|
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.IO as T
|
||||||
|
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
import Data.List.NonEmpty ( NonEmpty(..) )
|
||||||
|
|
||||||
|
import Control.Arrow ( (>>>) )
|
||||||
|
|
||||||
|
data EvaluationArgs = EvaluationArgs
|
||||||
|
{ evalUid :: Int
|
||||||
|
, evalModules :: NonEmpty FilePath
|
||||||
|
, evalPkgs :: Text
|
||||||
|
, evalWorkDir :: Maybe FilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate :: EvaluationArgs -> IO Value
|
||||||
|
evaluate ea = do
|
||||||
|
evalComposition <- getDataFileName "nix/eval-composition.nix"
|
||||||
|
let args =
|
||||||
|
[ evalComposition
|
||||||
|
, "--eval"
|
||||||
|
, "--strict"
|
||||||
|
, "--read-write-mode"
|
||||||
|
, "--json"
|
||||||
|
, "--show-trace"
|
||||||
|
, "--argstr"
|
||||||
|
, "uid"
|
||||||
|
, show $ evalUid ea
|
||||||
|
, "--arg"
|
||||||
|
, "modules"
|
||||||
|
, modulesNixExpr $ evalModules ea
|
||||||
|
, "--arg"
|
||||||
|
, "pkgs"
|
||||||
|
, toS $ evalPkgs ea
|
||||||
|
, "--attr"
|
||||||
|
, "config.build.dockerComposeYamlAttrs"
|
||||||
|
]
|
||||||
|
stdin = mempty
|
||||||
|
procSpec = (proc "nix-instantiate" args) { cwd = evalWorkDir ea }
|
||||||
|
|
||||||
|
-- TODO: lazy IO is tricky. Let's use conduit/pipes instead?
|
||||||
|
(exitCode, out, err) <- PBL.readCreateProcessWithExitCode procSpec stdin
|
||||||
|
|
||||||
|
-- Stream 'err'
|
||||||
|
errDone <- async (BL.hPutStr stderr err)
|
||||||
|
|
||||||
|
-- Force 'out'
|
||||||
|
v <- Protolude.evaluate (eitherDecode out)
|
||||||
|
|
||||||
|
-- Wait for process exit and 'err' printout
|
||||||
|
wait errDone
|
||||||
|
|
||||||
|
case exitCode of
|
||||||
|
ExitSuccess -> pass
|
||||||
|
ExitFailure e -> throwIO $ FatalError "Evaluation failed" -- TODO: don't print this exception in main
|
||||||
|
|
||||||
|
case v of
|
||||||
|
Right r -> pure r
|
||||||
|
Left e -> throwIO $ FatalError "Couldn't parse nix-instantiate output"
|
||||||
|
|
||||||
|
|
||||||
|
modulesNixExpr :: NonEmpty FilePath -> [Char]
|
||||||
|
modulesNixExpr =
|
||||||
|
NE.toList >>> fmap pathExpr >>> Data.String.unwords >>> wrapList
|
||||||
|
where
|
||||||
|
pathExpr :: FilePath -> [Char]
|
||||||
|
pathExpr path | isAbsolute path = "(/. + \"/${" <> toNixStringLiteral path <> "}\")"
|
||||||
|
| otherwise = "(./. + \"/${" <> toNixStringLiteral path <> "}\")"
|
||||||
|
|
||||||
|
isAbsolute ('/' : _) = True
|
||||||
|
isAbsolute _ = False
|
||||||
|
|
||||||
|
wrapList s = "[ " <> s <> " ]"
|
||||||
|
|
||||||
|
toNixStringLiteral :: [Char] -> [Char]
|
||||||
|
toNixStringLiteral = show -- FIXME: custom escaping including '$'
|
|
@ -19,10 +19,17 @@ in
|
||||||
build.dockerComposeYaml = lib.mkOption {
|
build.dockerComposeYaml = lib.mkOption {
|
||||||
type = lib.types.package;
|
type = lib.types.package;
|
||||||
description = "A derivation that produces a docker-compose.yaml file for this composition.";
|
description = "A derivation that produces a docker-compose.yaml file for this composition.";
|
||||||
|
readOnly = true;
|
||||||
};
|
};
|
||||||
build.dockerComposeYamlText = lib.mkOption {
|
build.dockerComposeYamlText = lib.mkOption {
|
||||||
type = lib.types.string;
|
type = lib.types.string;
|
||||||
description = "The text of build.dockerComposeYaml.";
|
description = "The text of build.dockerComposeYaml.";
|
||||||
|
readOnly = true;
|
||||||
|
};
|
||||||
|
build.dockerComposeYamlAttrs = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf lib.types.unspecified;
|
||||||
|
description = "The text of build.dockerComposeYaml.";
|
||||||
|
readOnly = true;
|
||||||
};
|
};
|
||||||
docker-compose.raw = lib.mkOption {
|
docker-compose.raw = lib.mkOption {
|
||||||
type = lib.types.attrs;
|
type = lib.types.attrs;
|
||||||
|
@ -45,7 +52,8 @@ in
|
||||||
};
|
};
|
||||||
config = {
|
config = {
|
||||||
build.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.build.dockerComposeYamlText;
|
build.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.build.dockerComposeYamlText;
|
||||||
build.dockerComposeYamlText = builtins.toJSON (config.docker-compose.raw);
|
build.dockerComposeYamlText = builtins.toJSON (config.build.dockerComposeYamlAttrs);
|
||||||
|
build.dockerComposeYamlAttrs = config.docker-compose.raw;
|
||||||
|
|
||||||
docker-compose.evaluatedServices = lib.mapAttrs evalService config.docker-compose.services;
|
docker-compose.evaluatedServices = lib.mapAttrs evalService config.docker-compose.services;
|
||||||
docker-compose.raw = {
|
docker-compose.raw = {
|
||||||
|
|
Loading…
Reference in a new issue