hercules-ci / arion

Run docker-compose with help from Nix/NixOS
Apache License 2.0
617 stars 48 forks source link

Improved flake support #129

Open colinxs opened 3 years ago

colinxs commented 3 years ago

I recently came across Arion and it's been fantastic. Thanks for making this package!

The only thing I've come across that's a bit awkward is the need to have arion-compose.nix and arion-pkgs.nix when using nixUnstable/flakes. Currently I have a dozen arion-{compose,pkgs}.nix scattered about that only contain a single line:

arion-pkgs.nix:

(builtins.getFlake (toString ./.)).legacyPackages.x86_64-linux

arion-compose.nix

(builtins.getFlake (toString ./.)).packages.x86_64-linux.arion-compose

Instead, it would be awesome if the following was possible:

arion --flake .#arion --flake /home/colinxs/fooflake#arion-foo up

This mimics the flake interface for nixos-rebuild, home-manager, and other tools in the Nix ecosystem.

Each flake.nix would look something like:

{
  inputs.nixpkgs.url = "nixpkgs/nixos-unstable";
  inputs.flake-utils.url = "github:numtide/flake-utils";
  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
      let pkgs = nixpkgs.legacyPackages."${system}";
      in
      {
        packages.arion = {
          inherit pkgs;
          config = { pkgs, ... }: {
            config.services = {
              webserver = {
                service.useHostStore = true;
                service.command = [
                  "sh"
                  "-c"
                  ''
                    cd "$$WEB_ROOT"
                    ${pkgs.python3}/bin/python -m http.server
                  ''
                ];
                service.ports = [
                  "8000:8000" # host:container
                ];
                service.environment.WEB_ROOT =
                  "${pkgs.nix.doc}/share/doc/nix/manual";
              };
            };
          };
        };
      });
}

Now everything is fully contained in the flake.nix without having to have arion-{compose,pkgs}.nix and you could do cool things like:

arion --flake github:colinxs/arion#arion-foo up

I tried to implement this myself and got about halfway there, but I know nothing about Haskell lol. If someone could help on the Haskell side I'd be happy to pitch in elsewhere.

roberth commented 3 years ago

Yes, this needs to be done. My thoughts so far:

We should use a new top-level flake attribute though, because arion projects aren't packages, and shouldn't even be derivations. There's a parallel with nixosConfigurations, but it's better to omit the "lib.nixosSystem" equivalent, because I don't consider the compose file derivation to be a sufficiently stable interface. There's no real need for it to be a derivation in the first place and it only complicates cross-system deployments.

Something like arionConfigurations.${system}.${projectName} makes sense for local development, but for real-world deployments, the user's "choice" of system should have no effect on what's deployed, so it should be a flat flake attr like arionConfigurations.${projectName}. These can't coexist in the same attr, so we can change the first one to localArionConfigurations.${system}.${projectName}.

Arion modules can be imported at the project, service or NixOS level. The latter isn't arion-specific, so for reusable arion modules in flakes we only need arionProjectModules and arionServiceModules, analogous to nixosModules, which we'll also reuse.

I did think about putting the derivation in a flake attribute anyway, but I just don't think it's constant enough. It'd break uid and therefore cause issues with user volume mounts. I'd also like to implement setting config values via the command line, like an improved docker-compose --env, which makes the arion projects even more like functions and less like enumerable derivations.

frantisekhanzlikbl commented 2 years ago

Just a tip for anyone who stumbles across this: since builtins.getFlake copies the whole repository into the nix store, it is not an acceptable solution for me due to performance reasons. flake-compat can be used to achieve the same result without the copying:

arion-compose.nix

(import
    (
        let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
        fetchTarball {
            url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
            sha256 = lock.nodes.flake-compat.locked.narHash;
        }
    )
    { src = ./.; }
).defaultNix.packages.x86_64-linux.arion-compose
roberth commented 2 years ago

builtins.getFlake copies the whole repository in to the nix store

It seems that https://github.com/NixOS/nix/pull/6530 may solve this.

roberth commented 2 years ago

My thoughts so far:

We might actually skip the arionConfigurations attr and work with arionModules instead. arionConfiguration would only serve to finalize and instantiate configurations, which can be done equally well at the use site, such as the arion program, an Hercules CI effect, a deploy script in packages or apps, or a flake-parts module. This avoids the issues in my previous comment by promoting interaction with the modules, which are composable.