diff --git a/arion-compose.cabal b/arion-compose.cabal index ac1c333..2920bc6 100644 --- a/arion-compose.cabal +++ b/arion-compose.cabal @@ -49,6 +49,7 @@ library exposed-modules: Arion.Nix Arion.Aeson Arion.DockerCompose + Arion.ExtendedInfo Arion.Images Arion.Services other-modules: Paths_arion_compose diff --git a/examples/minimal/arion-compose.nix b/examples/minimal/arion-compose.nix index ab3c7e8..e864794 100644 --- a/examples/minimal/arion-compose.nix +++ b/examples/minimal/arion-compose.nix @@ -1,5 +1,6 @@ { pkgs, ... }: { + config.name = "webapp"; config.services = { webserver = { diff --git a/src/haskell/exe/Main.hs b/src/haskell/exe/Main.hs index 1816cd4..ca219c9 100644 --- a/src/haskell/exe/Main.hs +++ b/src/haskell/exe/Main.hs @@ -10,6 +10,7 @@ import Arion.Aeson import Arion.Images (loadImages) import qualified Arion.DockerCompose as DockerCompose import Arion.Services (getDefaultExec) +import Arion.ExtendedInfo (loadExtendedInfoFromPath, ExtendedInfo(images, name)) import Options.Applicative import Control.Monad.Fail @@ -142,20 +143,24 @@ runDC cmd (DockerComposeArgs args) _opts = do runBuildAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO () runBuildAndDC cmd dopts opts = do - withBuiltComposeFile opts $ \path -> do - loadImages path - DockerCompose.run DockerCompose.Args - { files = [path] - , otherArgs = [cmd] ++ unDockerComposeArgs dopts - } + withBuiltComposeFile opts $ callDC cmd dopts True runEvalAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO () runEvalAndDC cmd dopts opts = do - withComposeFile opts $ \path -> - DockerCompose.run DockerCompose.Args - { files = [path] - , otherArgs = [cmd] ++ unDockerComposeArgs dopts - } + withComposeFile opts $ callDC cmd dopts False + +callDC :: Text -> DockerComposeArgs -> Bool -> FilePath -> IO () +callDC cmd dopts shouldLoadImages path = do + extendedInfo <- loadExtendedInfoFromPath path + when shouldLoadImages $ loadImages (images extendedInfo) + let firstOpts = + do + n <- toList (name extendedInfo) + ["--project-name", n] + DockerCompose.run DockerCompose.Args + { files = [path] + , otherArgs = firstOpts ++ [cmd] ++ unDockerComposeArgs dopts + } withBuiltComposeFile :: CommonOptions -> (FilePath -> IO r) -> IO r withBuiltComposeFile opts cont = case prebuiltComposeFile opts of diff --git a/src/haskell/lib/Arion/ExtendedInfo.hs b/src/haskell/lib/Arion/ExtendedInfo.hs new file mode 100644 index 0000000..dbdf338 --- /dev/null +++ b/src/haskell/lib/Arion/ExtendedInfo.hs @@ -0,0 +1,37 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE OverloadedStrings #-} +{- + +Parses the x-arion field in the generated compose file. + +-} +module Arion.ExtendedInfo where + +import Prelude() +import Protolude +import Data.Aeson as Aeson +import Arion.Aeson +import Control.Lens +import Data.Aeson.Lens + +data Image = Image + { image :: Maybe Text -- ^ image tar.gz file path + , imageExe :: Maybe Text -- ^ path to exe producing image tar + , imageName :: Text + , imageTag :: Text + } deriving (Eq, Show, Generic, Aeson.ToJSON, Aeson.FromJSON) + +data ExtendedInfo = ExtendedInfo { + name :: Maybe Text, + images :: [Image] + } deriving (Eq, Show) + +loadExtendedInfoFromPath :: FilePath -> IO ExtendedInfo +loadExtendedInfoFromPath fp = do + v <- decodeFile fp + pure ExtendedInfo { + -- TODO: use aeson derived instance? + name = v ^? key "x-arion" . key "name" . _String, + images = (v :: Aeson.Value) ^.. key "x-arion" . key "images" . _Array . traverse . _JSON + } diff --git a/src/haskell/lib/Arion/Images.hs b/src/haskell/lib/Arion/Images.hs index 40e0108..350ac93 100644 --- a/src/haskell/lib/Arion/Images.hs +++ b/src/haskell/lib/Arion/Images.hs @@ -8,38 +8,23 @@ module Arion.Images import Prelude() import Protolude hiding (to) -import qualified Data.Aeson as Aeson -import Arion.Aeson (decodeFile) import qualified System.Process as Process import qualified Data.Text as T -import Control.Lens -import Data.Aeson.Lens - -data Image = Image - { image :: Maybe Text -- ^ image tar.gz file path - , imageExe :: Maybe Text -- ^ path to exe producing image tar - , imageName :: Text - , imageTag :: Text - } deriving (Generic, Aeson.ToJSON, Aeson.FromJSON, Show) +import Arion.ExtendedInfo (Image(..)) type TaggedImage = Text -- | Subject to change -loadImages :: FilePath -> IO () -loadImages fp = do - - v <- decodeFile fp +loadImages :: [Image] -> IO () +loadImages requestedImages = do loaded <- getDockerImages let - images :: [Image] - images = (v :: Aeson.Value) ^.. key "x-arion" . key "images" . _Array . traverse . _JSON - isNew i = (imageName i <> ":" <> imageTag i) `notElem` loaded - traverse_ loadImage . filter isNew $ images + traverse_ loadImage . filter isNew $ requestedImages loadImage :: Image -> IO () loadImage (Image { image = Just imgPath, imageName = name }) = diff --git a/src/nix/modules.nix b/src/nix/modules.nix index 64f3650..b176b2e 100644 --- a/src/nix/modules.nix +++ b/src/nix/modules.nix @@ -4,4 +4,5 @@ ./modules/composition/images.nix ./modules/composition/service-info.nix ./modules/composition/arion-base-image.nix + ./modules/composition/composition.nix ] \ No newline at end of file diff --git a/src/nix/modules/composition/composition.nix b/src/nix/modules/composition/composition.nix new file mode 100644 index 0000000..fe4daf9 --- /dev/null +++ b/src/nix/modules/composition/composition.nix @@ -0,0 +1,24 @@ +{ config, lib, ... }: +let + inherit (lib) types mkOption; + + link = url: text: + ''link:${url}[${text}]''; + +in +{ + options = { + name = mkOption { + description = '' + Name of the project. + + See ${link "https://docs.docker.com/compose/reference/envvars/#compose_project_name" "COMPOSE_PROJECT_NAME"} + ''; + type = types.nullOr types.str; + default = null; + }; + }; + config = { + docker-compose.extended.name = config.name; + }; +}