arion-compose 0.2.0.0 → 0.2.1.0
raw patch · 15 files changed
+172/−84 lines, 15 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- CHANGELOG.md +14/−2
- arion-compose.cabal +2/−2
- src/haskell/lib/Arion/Services.hs +14/−1
- src/haskell/testdata/Arion/NixSpec/arion-compose.json +1/−0
- src/nix/lib.nix +8/−5
- src/nix/modules/composition/composition.nix +1/−1
- src/nix/modules/composition/docker-compose.nix +6/−0
- src/nix/modules/composition/host-environment.nix +3/−3
- src/nix/modules/composition/images.nix +1/−1
- src/nix/modules/composition/networks.nix +2/−2
- src/nix/modules/networks/network.nix +10/−10
- src/nix/modules/service/docker-compose-service.nix +90/−49
- src/nix/modules/service/extended-info.nix +1/−1
- src/nix/modules/service/host-store.nix +1/−1
- src/nix/modules/service/image.nix +18/−6
CHANGELOG.md view
@@ -1,11 +1,23 @@ # Revision history for Arion -## Next 0.2.0.0+## 0.2.1.0 -- 2023-07-26 +### Added++* `service.networks` now supports attribute set values with various options, thanks to @pedorich-n.+* `docker-compose.volumes` can now be specified in multiple modules, thanks to @qaifshaikh.+* `image.fakeRootCommands` for making modifications to the image that aren't "add a link farm".++### Fixed++* Regular maintenance fixes, including one by olebedev+++## 0.2.0.0 -- 2022-12-02+ ### BREAKING * The `project.name` option is now mandatory for projects that aren't deployed with the NixOS module.- * The NixOS module now sets the default network name to the project name (commonly referred to as `<name>` in the option path). If this is not desired, for instance if you need the projects to be on the same network, set `networks.default.name` in each of them.
arion-compose.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 name: arion-compose-version: 0.2.0.0+version: 0.2.1.0 synopsis: Run docker-compose with help from Nix/NixOS 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@@ -30,7 +30,7 @@ location: https://github.com/hercules-ci/arion common common- build-depends: base >=4.12.0.0 && <4.17+ build-depends: base >=4.12.0.0 && <4.99 , aeson >=2 , aeson-pretty , async
src/haskell/lib/Arion/Services.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-} module Arion.Services ( getDefaultExec ) where@@ -9,16 +10,28 @@ import Protolude hiding (to) import qualified Data.Aeson as Aeson+#if MIN_VERSION_lens_aeson(1,2,0) import qualified Data.Aeson.Key as AK+#endif import Arion.Aeson (decodeFile) import Control.Lens import Data.Aeson.Lens +#if MIN_VERSION_lens_aeson(1,2,0)+type Key = AK.Key+mkKey :: Text -> Key+mkKey = AK.fromText+#else+type Key = Text+mkKey :: Text -> Key+mkKey = identity+#endif+ -- | Subject to change getDefaultExec :: FilePath -> Text -> IO [Text] getDefaultExec fp service = do v <- decodeFile fp - pure ((v :: Aeson.Value) ^.. key "x-arion" . key "serviceInfo" . key (AK.fromText service) . key "defaultExec" . _Array . traverse . _String)+ pure ((v :: Aeson.Value) ^.. key "x-arion" . key "serviceInfo" . key (mkKey service) . key "defaultExec" . _Array . traverse . _String)
src/haskell/testdata/Arion/NixSpec/arion-compose.json view
@@ -33,6 +33,7 @@ } }, "version": "3.4",+ "volumes": {}, "x-arion": { "images": [ {
src/nix/lib.nix view
@@ -1,16 +1,19 @@ { lib }: let - link = url: text:- ''link:${url}[${text}]'';+ link = url: text: ''[${text}](${url})''; - dockerComposeRef = fragment:- ''See ${link "https://docs.docker.com/compose/compose-file/#${fragment}" "Docker Compose#${fragment}"}'';+ serviceRef = fragment:+ ''See ${link "https://docs.docker.com/compose/compose-file/05-services/#${fragment}" "Docker Compose Services #${fragment}"}''; + networkRef = fragment:+ ''See ${link "https://docs.docker.com/compose/compose-file/06-networks/#${fragment}" "Docker Compose Network #${fragment}"}'';+ in { inherit- dockerComposeRef link+ networkRef+ serviceRef ; }
src/nix/modules/composition/composition.nix view
@@ -3,7 +3,7 @@ inherit (lib) types mkOption; link = url: text:- ''link:${url}[${text}]'';+ ''[${text}](${url})''; in {
src/nix/modules/composition/docker-compose.nix view
@@ -63,6 +63,11 @@ type = lib.types.attrsOf (lib.types.submodule service); description = "An attribute set of service configurations. A service specifies how to run an image as a container."; };+ docker-compose.volumes = lib.mkOption {+ type = lib.types.attrsOf lib.types.unspecified;+ description = "A attribute set of volume configurations.";+ default = {};+ }; }; config = { out.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.out.dockerComposeYamlText;@@ -73,6 +78,7 @@ version = "3.4"; services = lib.mapAttrs (k: c: c.out.service) config.services; x-arion = config.docker-compose.extended;+ volumes = config.docker-compose.volumes; }; }; }
src/nix/modules/composition/host-environment.nix view
@@ -23,9 +23,9 @@ stored at an alternate location without altering the format of store paths. - For example: instead of mounting the host's /nix/store as the- container's /nix/store, this will mount /mnt/foo/nix/store- as the container's /nix/store.+ For example: instead of mounting the host's `/nix/store` as the+ container's `/nix/store`, this will mount `/mnt/foo/nix/store`+ as the container's `/nix/store`. ''; };
src/nix/modules/composition/images.nix view
@@ -36,7 +36,7 @@ build.imagesToLoad = lib.mkOption { type = listOf unspecified; internal = true;- description = "List of dockerTools image derivations.";+ description = "List of `dockerTools` image derivations."; }; }; config = {
src/nix/modules/composition/networks.nix view
@@ -7,7 +7,7 @@ types ; inherit (import ../../lib.nix { inherit lib; })- dockerComposeRef+ link ; in {@@ -19,7 +19,7 @@ ]; }); description = ''- ${dockerComposeRef "networks-top-level-element"}+ See ${link "https://docs.docker.com/compose/compose-file/06-networks/" "Docker Compose Networks"} ''; }; enableDefaultNetwork = mkOption {
src/nix/modules/networks/network.nix view
@@ -7,7 +7,7 @@ types ; inherit (import ../../lib.nix { inherit lib; })- dockerComposeRef+ networkRef ; in {@@ -15,21 +15,21 @@ driver = mkOption { description = '' `"none"`, `"host"`, or a platform-specific value.- ${dockerComposeRef "driver"}+ ${networkRef "driver"} ''; type = types.str; }; driver_opts = mkOption { description = ''- ${dockerComposeRef "driver_opts"}+ ${networkRef "driver_opts"} ''; type = types.lazyAttrsOf types.raw or types.unspecified; }; attachable = mkOption { description = ''- ${dockerComposeRef "attachable"}+ ${networkRef "attachable"} ''; type = types.bool; example = true;@@ -39,7 +39,7 @@ description = '' Whether we've entered the 21st century yet. - ${dockerComposeRef "enable_ipv6"}+ ${networkRef "enable_ipv6"} ''; type = types.bool; };@@ -49,7 +49,7 @@ description = '' Manage IP addresses. - ${dockerComposeRef "ipam"}+ ${networkRef "ipam"} ''; type = types.raw or types.unspecified; };@@ -58,7 +58,7 @@ description = '' Achieves "external isolation". - ${dockerComposeRef "internal"}+ ${networkRef "internal"} ''; defaultText = false; type = types.bool;@@ -68,7 +68,7 @@ description = '' Metadata. - ${dockerComposeRef "labels"}+ ${networkRef "labels"} ''; # no list support, because less expressive wrt overriding type = types.attrsOf types.str;@@ -79,7 +79,7 @@ When `true`, don't create or destroy the network, but assume that it exists. - ${dockerComposeRef "external"}+ ${networkRef "external"} ''; type = types.bool; };@@ -92,7 +92,7 @@ Note the `default` network's default `name` is set to `project.name` by Arion. - ${dockerComposeRef "name"}+ ${networkRef "name"} ''; type = types.str; };
src/nix/modules/service/docker-compose-service.nix view
@@ -12,15 +12,9 @@ inherit (import ../../lib.nix { inherit lib; }) link- dockerComposeRef+ serviceRef ; - dockerComposeKitchenSink = ''- Analogous to the `docker run` counterpart.-- ${dockerComposeRef "domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir"}- '';- cap_add = lib.attrNames (lib.filterAttrs (name: value: value == true) config.service.capabilities); cap_drop = lib.attrNames (lib.filterAttrs (name: value: value == false) config.service.capabilities); @@ -56,12 +50,12 @@ service.volumes = mkOption { type = listOf types.unspecified; default = [];- description = dockerComposeRef "volumes";+ description = serviceRef "volumes"; }; service.tmpfs = mkOption { type = listOf types.str; default = [];- description = dockerComposeRef "tmpfs";+ description = serviceRef "tmpfs"; }; service.build.context = mkOption { type = nullOr str;@@ -69,44 +63,48 @@ description = '' Locates a Dockerfile to use for creating an image to use in this service. - ${dockerComposeRef "context"}+ https://docs.docker.com/compose/compose-file/build/#context ''; }; service.hostname = mkOption { type = nullOr str; default = null;- description = dockerComposeKitchenSink;+ description = ''+ ${serviceRef "hostname"}+ ''; }; service.tty = mkOption { type = nullOr bool; default = null;- description = dockerComposeKitchenSink;+ description = ''+ ${serviceRef "tty"}+ ''; }; service.environment = mkOption { type = attrsOf (either str int); default = {};- description = dockerComposeRef "environment";+ description = serviceRef "environment"; }; service.image = mkOption { type = str;- description = dockerComposeRef "image";+ description = serviceRef "image"; }; service.command = mkOption { type = nullOr types.unspecified; default = null;- description = dockerComposeRef "command";+ description = serviceRef "command"; }; service.container_name = mkOption { type = nullOr types.str; default = null;- description = dockerComposeRef "container_name";+ description = serviceRef "container_name"; }; service.depends_on = let conditionsModule = { options = { condition = mkOption { type = enum ["service_started" "service_healthy" "service_completed_successfully"];- description = dockerComposeRef "depends_on";+ description = serviceRef "depends_on"; default = "service_started"; }; };@@ -114,10 +112,10 @@ in mkOption { type = either (listOf str) (attrsOf (submodule conditionsModule)); default = [];- description = dockerComposeRef "depends_on";+ description = serviceRef "depends_on"; }; service.healthcheck = mkOption {- description = dockerComposeRef "healthcheck";+ description = serviceRef "healthcheck"; type = submodule ({ config, options, ...}: { options = { _out = mkOption {@@ -130,30 +128,30 @@ type = nullOr (listOf str); default = null; example = [ "CMD" "pg_isready" ];- description = dockerComposeRef "healthcheck";+ description = serviceRef "healthcheck"; }; interval = mkOption { type = str; default = "30s"; example = "1m";- description = dockerComposeRef "healthcheck";+ description = serviceRef "healthcheck"; }; timeout = mkOption { type = str; default = "30s"; example = "10s";- description = dockerComposeRef "healthcheck";+ description = serviceRef "healthcheck"; }; start_period = mkOption { type = str; default = "0s"; example = "30s";- description = dockerComposeRef "healthcheck";+ description = serviceRef "healthcheck"; }; retries = mkOption { type = int; default = 3;- description = dockerComposeRef "healthcheck";+ description = serviceRef "healthcheck"; }; }; });@@ -165,14 +163,14 @@ See ${link "https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities" "`docker run --device` documentation"} - ${dockerComposeRef "devices"}+ ${serviceRef "devices"} ''; }; service.dns = mkOption { type = listOf str; default = []; example = [ "8.8.8.8" "8.8.4.4" ];- description = dockerComposeRef "dns";+ description = serviceRef "dns"; }; service.labels = mkOption { type = attrsOf str;@@ -183,47 +181,53 @@ "traefik.http.routers.my-service.rule" = "Host(`my-service.localhost`)"; "traefik.http.routers.my-service.entrypoints" = "web"; };- description = dockerComposeRef "labels";+ description = serviceRef "labels"; }; service.links = mkOption { type = listOf str; default = [];- description = dockerComposeRef "links";+ description = serviceRef "links"; }; service.external_links = mkOption { type = listOf str; default = [];- description = dockerComposeRef "external_links";+ description = serviceRef "external_links"; }; service.extra_hosts = mkOption { type = listOf str; default = [];- description = dockerComposeRef "extra_hosts";+ description = serviceRef "extra_hosts"; }; service.working_dir = mkOption { type = nullOr str; default = null;- description = dockerComposeKitchenSink;+ description = ''+ ${serviceRef "working_dir"}+ ''; }; service.privileged = mkOption { type = nullOr bool; default = null;- description = dockerComposeKitchenSink;+ description = ''+ ${serviceRef "privileged"}+ ''; }; service.entrypoint = mkOption { type = nullOr str; default = null;- description = dockerComposeRef "entrypoint";+ description = serviceRef "entrypoint"; }; service.restart = mkOption { type = nullOr str; default = null;- description = dockerComposeRef "restart";+ description = serviceRef "restart"; }; service.user = mkOption { type = nullOr str; default = null;- description = dockerComposeKitchenSink;+ description = ''+ ${serviceRef "user"}+ ''; }; service.ports = mkOption { type = listOf types.unspecified;@@ -231,38 +235,71 @@ description = '' Expose ports on host. "host:container" or structured. - ${dockerComposeRef "ports"}+ ${serviceRef "ports"} ''; }; service.expose = mkOption { type = listOf str; default = [];- description = dockerComposeRef "expose";+ description = serviceRef "expose"; }; service.env_file = mkOption { type = listOf str; default = [];- description = dockerComposeRef "env_file";+ description = serviceRef "env_file"; }; service.network_mode = mkOption { type = nullOr str; default = null;- description = dockerComposeRef "network_mode";- };- service.networks = mkOption {- type = nullOr (listOf types.str);- default = null;- description = dockerComposeRef "networks";+ description = serviceRef "network_mode"; };+ service.networks =+ let+ networksModule = submodule ({ config, options, ...}: {+ options = {+ _out = mkOption {+ internal = true;+ readOnly = true;+ default = lib.mapAttrs (k: opt: opt.value) (lib.filterAttrs (_: opt: opt.isDefined) { inherit (options) aliases ipv4_address ipv6_address link_local_ips priority; });+ };+ aliases = mkOption {+ type = listOf str;+ description = serviceRef "aliases";+ default = [ ];+ };+ ipv4_address = mkOption {+ type = str;+ description = serviceRef "ipv4_address-ipv6_address";+ };+ ipv6_address = mkOption {+ type = str;+ description = serviceRef "ipv4_address-ipv6_address";+ };+ link_local_ips = mkOption {+ type = listOf str;+ description = serviceRef "link_local_ips";+ };+ priority = mkOption {+ type = int;+ description = serviceRef "priority";+ };+ };+ });+ in+ mkOption {+ type = either (listOf str) (attrsOf networksModule);+ default = [];+ description = serviceRef "networks";+ }; service.stop_signal = mkOption { type = nullOr str; default = null;- description = dockerComposeRef "stop_signal";+ description = serviceRef "stop_signal"; }; service.sysctls = mkOption { type = attrsOf (either str int); default = {};- description = dockerComposeRef "sysctls";+ description = serviceRef "sysctls"; }; service.capabilities = mkOption { type = attrsOf (nullOr bool);@@ -273,13 +310,15 @@ Setting a capability to `true` means that it will be "added". Setting it to `false` means that it will be "dropped".- ${dockerComposeRef "cap_add-cap_drop"} Omitted and `null` capabilities will therefore be set according to Docker's ${ link "https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities" "default list of capabilities." }++ ${serviceRef "cap_add"}+ ${serviceRef "cap_drop"} ''; }; };@@ -331,8 +370,10 @@ inherit (config.service) privileged; } // lib.optionalAttrs (config.service.network_mode != null) { inherit (config.service) network_mode;- } // lib.optionalAttrs (config.service.networks != null) {- inherit (config.service) networks;+ } // lib.optionalAttrs (config.service.networks != [] && config.service.networks != {}) {+ networks =+ if (builtins.isAttrs config.service.networks) then builtins.mapAttrs (_: v: v._out) config.service.networks+ else config.service.networks; } // lib.optionalAttrs (config.service.restart != null) { inherit (config.service) restart; } // lib.optionalAttrs (config.service.stop_signal != null) {
src/nix/modules/service/extended-info.nix view
@@ -12,7 +12,7 @@ type = attrsOf unspecified; description = '' Information about a service to include in the Docker Compose file,- but that will not be used by the `docker-compose`> command+ but that will not be used by the `docker-compose` command itself. It will be inserted in `x-arion.serviceInfo.<service.name>`.
src/nix/modules/service/host-store.nix view
@@ -20,7 +20,7 @@ service.hostStoreAsReadOnly = mkOption { type = types.bool; default = true;- description = "Adds a ':ro' (read-only) access mode to the host nix store bind mount.";+ description = "Adds a `:ro` (read-only) access mode to the host nix store bind mount."; }; service.useHostNixDaemon = mkOption { type = types.bool;
src/nix/modules/service/image.nix view
@@ -30,6 +30,7 @@ { name = null; tag = null; contents = null; config = null; created = null; extraCommands = null; maxLayers = null;+ fakeRootCommands = null; } args; acceptedArgs = functionArgs dockerTools.streamLayeredImage;@@ -67,6 +68,8 @@ ln -s $i nix/var/nix/gcroots/docker/$(basename $i) done; '';++ fakeRootCommands = config.image.fakeRootCommands; }; priorityIsDefault = option: option.highestPrio >= (lib.mkDefault true).priority;@@ -76,18 +79,18 @@ build.image = mkOption { type = nullOr package; description = ''- Docker image derivation to be `docker load`ed.+ Docker image derivation to be `docker load`-ed. ''; internal = true; }; build.imageName = mkOption { type = str;- description = "Derived from build.image";+ description = "Derived from `build.image`"; internal = true; }; build.imageTag = mkOption { type = str;- description = "Derived from build.image";+ description = "Derived from `build.image`"; internal = true; }; image.nixBuild = mkOption {@@ -120,13 +123,22 @@ Top level paths in the container. ''; };+ image.fakeRootCommands = mkOption {+ type = types.lines;+ default = "";+ description = ''+ Commands that build the root of the container in the current working directory.++ See [`dockerTools.buildLayeredImage`](https://nixos.org/manual/nixpkgs/stable/#ssec-pkgs-dockerTools-buildLayeredImage).+ '';+ }; image.includeStorePaths = mkOption { type = bool; default = true; internal = true; description = '' Include all referenced store paths. You generally want this in your- image, unless you load store paths via some other means, like useHostStore = true;+ image, unless you load store paths via some other means, like `useHostStore = true`; ''; }; image.rawConfig = mkOption {@@ -140,8 +152,8 @@ Please use the specific `image` options instead. Run-time configuration of the container. A full list of the- options is available in the https://github.com/moby/moby/blob/master/image/spec/v1.2.md#image-json-field-descriptions[Docker Image Specification- v1.2.0].+ options is available in the [Docker Image Specification+ v1.2.0](https://github.com/moby/moby/blob/master/image/spec/v1.2.md#image-json-field-descriptions). ''; }; image.command = mkOption {