From 7749eb2ef92fa9935fa77795a2ad61be34e436c0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 4 Oct 2019 19:09:29 +0200 Subject: [PATCH] Add cabal check to ci, build strictly (once) --- arion-compose.cabal | 22 ++++++++++++---------- nix/ci.nix | 9 ++++++--- nix/haskell-overlay.nix | 12 ++++++++++++ src/haskell/exe/Main.hs | 18 ++++++------------ src/haskell/lib/Arion/Aeson.hs | 2 -- src/haskell/lib/Arion/DockerCompose.hs | 20 +------------------- src/haskell/lib/Arion/Images.hs | 1 - src/haskell/lib/Arion/Nix.hs | 24 +++++++++--------------- src/haskell/lib/Arion/Services.hs | 4 ---- src/haskell/test/Arion/NixSpec.hs | 9 ++------- src/haskell/test/TestMain.hs | 1 + 11 files changed, 49 insertions(+), 73 deletions(-) diff --git a/arion-compose.cabal b/arion-compose.cabal index 2ff2f0b..853bb3a 100644 --- a/arion-compose.cabal +++ b/arion-compose.cabal @@ -1,9 +1,9 @@ cabal-version: 2.4 name: arion-compose -version: 0.1.0.0 +version: 0.1.0.0 synopsis: Run docker-compose with help from Nix/NixOS --- description: +description: Arion is a tool for building and running applications that consist of multiple docker containers using NixOS modules. It has special support for docker images that are built with Nix, for a smooth development experience and improved performance. homepage: https://github.com/hercules-ci/arion#readme -- bug-reports: license: Apache-2.0 @@ -11,10 +11,10 @@ license-file: LICENSE author: Robert Hensing maintainer: robert@hercules-ci.com -- copyright: --- category: -extra-source-files: CHANGELOG.md, README.asciidoc -write-ghc-enviroment-files: - never +category: Distribution, Network, Cloud, Distributed Computing +extra-source-files: CHANGELOG.md, README.asciidoc, + src/haskell/testdata/**/*.nix + src/haskell/testdata/**/*.json data-files: nix/*.nix , nix/modules/composition/*.nix , nix/modules/nixos/*.nix @@ -24,7 +24,7 @@ data-files: nix/*.nix -- all data is verbatim from some sources data-dir: src -common deps +common common build-depends: base ^>=4.12.0.0 , aeson , aeson-pretty @@ -38,25 +38,27 @@ common deps , text , protolude , unix + ghc-options: -Wall flag ghci default: False manual: True library - import: deps + import: common exposed-modules: Arion.Nix Arion.Aeson Arion.DockerCompose Arion.Images Arion.Services other-modules: Paths_arion_compose + autogen-modules: Paths_arion_compose -- other-extensions: hs-source-dirs: src/haskell/lib default-language: Haskell2010 executable arion - import: deps + import: common main-is: Main.hs -- other-modules: -- other-extensions: @@ -66,7 +68,7 @@ executable arion default-language: Haskell2010 test-suite arion-unit-tests - import: deps + import: common if flag(ghci) hs-source-dirs: src/haskell/lib ghc-options: -Wno-missing-home-modules diff --git a/nix/ci.nix b/nix/ci.nix index 4cf9309..a478fff 100644 --- a/nix/ci.nix +++ b/nix/ci.nix @@ -7,6 +7,7 @@ in dimension "Nixpkgs version" { "nixos-19_03" = { nixpkgsSource = "nixpkgs"; + isReferenceNixpkgs = true; }; "nixos-unstable" = { nixpkgsSource = "nixos-unstable"; @@ -16,15 +17,15 @@ dimension "Nixpkgs version" { enableDoc = false; }; } ( - _name: { nixpkgsSource, enableDoc ? true }: + _name: { nixpkgsSource, isReferenceNixpkgs ? false, enableDoc ? true }: dimension "System" { - "x86_64-linux" = {}; + "x86_64-linux" = { isReferenceTarget = isReferenceNixpkgs; }; # TODO: darwin # "x86_64-darwin" = { enableNixOSTests = false; }; } ( - system: {}: + system: { isReferenceTarget ? false }: let pkgs = import ./. { inherit system; nixpkgsSrc = sources.${nixpkgsSource}; }; in @@ -32,6 +33,8 @@ dimension "Nixpkgs version" { inherit (pkgs) arion tests; } // lib.optionalAttrs enableDoc { doc = pkgs.recurseIntoAttrs (import ../doc { inherit pkgs; }); + } // lib.optionalAttrs isReferenceTarget { + inherit (pkgs.arion-project.haskellPkgs) arion-compose-checked; } ) ) diff --git a/nix/haskell-overlay.nix b/nix/haskell-overlay.nix index c2d82cb..7d5ba27 100644 --- a/nix/haskell-overlay.nix +++ b/nix/haskell-overlay.nix @@ -1,4 +1,16 @@ self: super: hself: hsuper: { arion-compose = import ./haskell-arion-compose.nix { pkgs = self; haskellPackages = hself; }; + arion-compose-checked = + let pkg = super.haskell.lib.buildStrictly hself.arion-compose; + checked = super.haskell.lib.overrideCabal pkg (o: { + postConfigure = ''${o.postConfigure or ""} + if ! ${hsuper.cabal-install}/bin/cabal check; + then + echo 1>&2 ERROR: cabal file is invalid. Above warnings were errors. + exit 1 + fi + ''; + }); + in checked; } \ No newline at end of file diff --git a/src/haskell/exe/Main.hs b/src/haskell/exe/Main.hs index 57fcada..6f806c6 100644 --- a/src/haskell/exe/Main.hs +++ b/src/haskell/exe/Main.hs @@ -12,20 +12,13 @@ import qualified Arion.DockerCompose as DockerCompose import Arion.Services (getDefaultExec) import Options.Applicative -import Control.Applicative import Control.Monad.Fail -import qualified Data.Aeson.Encode.Pretty import qualified Data.Text 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 Data.List.NonEmpty (NonEmpty(..)) -import Control.Arrow ((>>>)) - import System.Posix.User (getRealUserID) data CommonOptions = @@ -69,6 +62,7 @@ parseOptions = do let nixArgs = userNixArgs <|> "--show-trace" <$ guard showTrace in CommonOptions{..} +textArgument :: Mod ArgumentFields [Char] -> Parser Text textArgument = fmap T.pack . strArgument parseCommand :: Parser (CommonOptions -> IO ()) @@ -124,18 +118,18 @@ commandDC -> Text -> Text -> Mod CommandFields (CommonOptions -> IO ()) -commandDC run cmdStr help = +commandDC run cmdStr helpText = command (T.unpack cmdStr) (info (run cmdStr <$> parseDockerComposeArgs) - (progDesc (T.unpack help) <> fullDesc <> forwardOptions)) + (progDesc (T.unpack helpText) <> fullDesc <> forwardOptions)) -------------------------------------------------------------------------------- runDC :: Text -> DockerComposeArgs -> CommonOptions -> IO () -runDC cmd (DockerComposeArgs args) opts = do +runDC cmd (DockerComposeArgs args) _opts = do DockerCompose.run DockerCompose.Args { files = [] , otherArgs = [cmd] ++ args @@ -265,7 +259,7 @@ runExec detach privileged user noTTY index envs workDir service commandAndArgs o main :: IO () main = - (join . execParser) (info (parseAll <**> helper) fullDesc) + (join . arionExecParser) (info (parseAll <**> helper) fullDesc) where - execParser = customExecParser (prefs showHelpOnEmpty) + arionExecParser = customExecParser (prefs showHelpOnEmpty) diff --git a/src/haskell/lib/Arion/Aeson.hs b/src/haskell/lib/Arion/Aeson.hs index dd3ae12..02c9b89 100644 --- a/src/haskell/lib/Arion/Aeson.hs +++ b/src/haskell/lib/Arion/Aeson.hs @@ -4,11 +4,9 @@ import Prelude () import Data.Aeson import qualified Data.ByteString.Lazy as BL 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 ) diff --git a/src/haskell/lib/Arion/DockerCompose.hs b/src/haskell/lib/Arion/DockerCompose.hs index b7cce7f..f44d86f 100644 --- a/src/haskell/lib/Arion/DockerCompose.hs +++ b/src/haskell/lib/Arion/DockerCompose.hs @@ -3,24 +3,7 @@ module Arion.DockerCompose where import Prelude ( ) import Protolude -import Arion.Aeson ( pretty ) -import Data.Aeson -import qualified Data.String import System.Process -import qualified Data.ByteString as BS -import qualified Data.ByteString.Lazy as BL -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 ( (>>>) ) -import System.IO.Temp ( withTempFile ) -import System.IO ( hClose ) data Args = Args { files :: [FilePath] @@ -43,6 +26,5 @@ run args = do case exitCode of ExitSuccess -> pass ExitFailure 1 -> exitFailure - e@ExitFailure {} -> do + ExitFailure {} -> do throwIO $ FatalError $ "docker-compose failed with " <> show exitCode - exitWith e diff --git a/src/haskell/lib/Arion/Images.hs b/src/haskell/lib/Arion/Images.hs index 369d09f..a533cec 100644 --- a/src/haskell/lib/Arion/Images.hs +++ b/src/haskell/lib/Arion/Images.hs @@ -10,7 +10,6 @@ import Protolude hiding (to) import qualified Data.Aeson as Aeson import Arion.Aeson (decodeFile) -import qualified Data.ByteString as BS import qualified System.Process as Process import Control.Lens diff --git a/src/haskell/lib/Arion/Nix.hs b/src/haskell/lib/Arion/Nix.hs index a90343a..dbc9103 100644 --- a/src/haskell/lib/Arion/Nix.hs +++ b/src/haskell/lib/Arion/Nix.hs @@ -16,12 +16,9 @@ import Data.Aeson import qualified Data.String import qualified System.Directory as Directory import System.Process -import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL 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 @@ -76,21 +73,20 @@ evaluateComposition ea = do case exitCode of ExitSuccess -> pass ExitFailure 1 -> exitFailure - e@ExitFailure {} -> do + ExitFailure {} -> do throwIO $ FatalError $ "evaluation failed with " <> show exitCode - exitWith e case v of Right r -> pure r - Left e -> throwIO $ FatalError "Couldn't parse nix-instantiate output" + Left e -> throwIO $ FatalError ("Couldn't parse nix-instantiate output" <> show e) -- | Run with docker-compose.yaml tmpfile withEvaluatedComposition :: EvaluationArgs -> (FilePath -> IO r) -> IO r withEvaluatedComposition ea f = do v <- evaluateComposition ea - withTempFile "." ".tmp-arion-docker-compose.yaml" $ \path handle -> do - T.hPutStrLn handle (pretty v) - hClose handle + withTempFile "." ".tmp-arion-docker-compose.yaml" $ \path yamlHandle -> do + T.hPutStrLn yamlHandle (pretty v) + hClose yamlHandle f path @@ -117,15 +113,14 @@ buildComposition outLink ea = do case exitCode of ExitSuccess -> pass ExitFailure 1 -> exitFailure - e@ExitFailure {} -> do + ExitFailure {} -> do throwIO $ FatalError $ "nix-build failed with " <> show exitCode - exitWith e -- | Do something with a docker-compose.yaml. withBuiltComposition :: EvaluationArgs -> (FilePath -> IO r) -> IO r withBuiltComposition ea f = do - withTempFile "." ".tmp-arion-docker-compose.yaml" $ \path handle -> do - hClose handle + withTempFile "." ".tmp-arion-docker-compose.yaml" $ \path emptyYamlHandle -> do + hClose emptyYamlHandle -- Known problem: kills atomicity of withTempFile; won't fix because we should manage gc roots, -- impl of which will probably avoid this "problem". It seems unlikely to cause issues. Directory.removeFile path @@ -149,9 +144,8 @@ replForComposition ea = do case exitCode of ExitSuccess -> pass ExitFailure 1 -> exitFailure - e@ExitFailure {} -> do + ExitFailure {} -> do throwIO $ FatalError $ "nix repl failed with " <> show exitCode - exitWith e argArgs :: EvaluationArgs -> [[Char]] argArgs ea = diff --git a/src/haskell/lib/Arion/Services.hs b/src/haskell/lib/Arion/Services.hs index f63e6e2..d8ecb30 100644 --- a/src/haskell/lib/Arion/Services.hs +++ b/src/haskell/lib/Arion/Services.hs @@ -10,13 +10,9 @@ import Protolude hiding (to) import qualified Data.Aeson as Aeson import Arion.Aeson (decodeFile) -import qualified Data.ByteString as BS -import qualified System.Process as Process import Control.Lens import Data.Aeson.Lens -import Data.String -import System.IO (withFile, IOMode(ReadMode)) -- | Subject to change getDefaultExec :: FilePath -> Text -> IO [Text] diff --git a/src/haskell/test/Arion/NixSpec.hs b/src/haskell/test/Arion/NixSpec.hs index 884a68a..f5c3887 100644 --- a/src/haskell/test/Arion/NixSpec.hs +++ b/src/haskell/test/Arion/NixSpec.hs @@ -6,16 +6,11 @@ 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 "evaluateComposition" $ it "matches an example" $ do @@ -32,8 +27,8 @@ spec = describe "evaluateComposition" $ it "matches an example" $ do expected <- T.readFile "src/haskell/testdata/Arion/NixSpec/arion-compose.json" censorPaths actual `shouldBe` censorPaths expected +censorPaths :: Text -> Text censorPaths = censorImages . censorStorePaths ---censorPaths = censorStorePaths censorStorePaths :: Text -> Text censorStorePaths x = case T.breakOn "/nix/store/" x of @@ -61,4 +56,4 @@ isNixNameChar c | c >= 'A' && c <= 'Z' = True isNixNameChar c | c == '-' = True isNixNameChar c | c == '.' = True isNixNameChar c | c == '_' = True -- WRONG? -isNixNameChar c = False -- WRONG? +isNixNameChar _ = False -- WRONG? diff --git a/src/haskell/test/TestMain.hs b/src/haskell/test/TestMain.hs index 746b8d7..e0303fa 100644 --- a/src/haskell/test/TestMain.hs +++ b/src/haskell/test/TestMain.hs @@ -1,5 +1,6 @@ module Main where +import Prelude() import Protolude import Test.Hspec.Runner import qualified Spec