packages feed

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 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 {