{ flake-parts-lib, lib, inputs, ... }: { options.perSystem = flake-parts-lib.mkPerSystemOption ( { config, pkgs, system, ... }: let cfg = config.ci.config; filterAttrsRec = pred: v: if lib.isAttrs v then lib.filterAttrs pred (lib.mapAttrs (path: filterAttrsRec pred) v) else v; subType = options: lib.types.submodule {inherit options;}; configType = with lib; subType { default-nix-image = mkOption { type = types.str; default = "registry.gitlab.com/technofab/nix-gitlab-ci/nix-ci:latest"; description = "The image to use on nix jobs"; }; nix-jobs-per-default = mkOption { type = types.bool; default = true; description = "Handle jobs nix-based by default or via opt-in (in job set nix = true) if false"; }; }; jobType = with lib; subType { # nix ci opts nix = mkOption { type = types.bool; default = cfg.nix-jobs-per-default; }; deps = mkOption { type = types.listOf types.package; default = []; }; # gitlab opts before_script = mkOption { type = types.listOf types.str; default = []; }; script = mkOption { type = types.listOf types.str; default = []; }; after_script = mkOption { type = types.listOf types.str; default = []; }; stage = mkOption { type = types.str; }; image = mkOption { type = types.str; default = cfg.default-nix-image; }; variables = mkOption { type = types.nullOr types.attrs; default = null; }; artifacts = mkOption { type = types.nullOr types.attrs; default = null; }; when = mkOption { type = types.nullOr types.str; default = null; }; start_in = mkOption { type = types.nullOr types.str; default = null; }; allow_failure = mkOption { type = types.nullOr types.bool; default = null; }; needs = mkOption { type = types.nullOr (types.listOf types.str); default = null; }; rules = mkOption { type = types.nullOr types.attrs; default = null; }; }; in { options = with lib; { ci = mkOption { type = subType { config = mkOption { type = configType; description = '' Configuration options for the nix part itself ''; default = {}; }; image = mkOption { type = types.nullOr types.str; default = null; }; variables = mkOption { type = types.nullOr types.attrs; default = null; }; default = mkOption { type = types.nullOr types.attrs; default = null; }; stages = mkOption { type = types.nullOr (types.listOf types.str); default = null; }; jobs = mkOption { type = types.lazyAttrsOf jobType; default = {}; }; }; description = '' Generate a Gitlab CI configuration which can be used to trigger a child pipeline. This will inject code which pre-downloads the nix deps before each job and adds them to PATH. ''; default = {}; }; }; config.packages = let toYaml = (pkgs.formats.yaml {}).generate; mapAttrs = cb: set: builtins.listToAttrs (builtins.map (key: cb key (builtins.getAttr key set)) (builtins.attrNames set)); prependToBeforeScript = arr: job: job // lib.optionalAttrs job.nix { before_script = arr ++ job.before_script; }; jobs = filterAttrsRec (n: v: v != null) config.ci.jobs; rest = filterAttrsRec (n: v: v != null) (builtins.removeAttrs config.ci ["jobs" "config"]); # this allows us to nix build this to get all the mentioned dependencies from the binary cache # pro: we don't have to download everything, just the deps for the current job # before, we just allowed pkgs inside the script string directly, but now with the ability to source this file # we can support different architectures between runners (eg. the arch of the initial runner does not matter) jobsMappedForDeps = mapAttrs (key: job: { name = "gitlab-ci-job-deps:${key}"; value = pkgs.writeShellScript "gitlab-ci-jobs:${key}" '' export PATH="${lib.makeBinPath job.deps}:$PATH"; ''; }) jobs; # allows the user to directly run the script jobsMappedForScript = mapAttrs (key: job: { name = "gitlab-ci-job:${key}"; value = pkgs.writeShellScriptBin "gitlab-ci-jobs:${key}" (lib.strings.concatLines (job.before_script ++ job.script ++ job.after_script)); }) jobs; # build the deps specific for this job before anything, this way the deps should be fetched from the cache jobsPatched = mapAttrs (key: job: { name = key; value = builtins.removeAttrs ( (prependToBeforeScript [ "echo -e \"\\e[0Ksection_start:`date +%s`:nix_deps[collapsed=true]\\r\\e[0KDownload Nix deps for job\"" "nix build .#gitlab-ci-job-deps:${key}" "source $(readlink -f result)" "echo -e \"\\e[0Ksection_end:`date +%s`:nix_deps\\r\\e[0K\"" ] job) // lib.optionalAttrs job.nix { image = job.image; } ) ["nix" "deps"]; }) jobs; in { gitlab-ci-config = toYaml "generated-gitlab-ci.yml" (rest // jobsPatched); } // jobsMappedForDeps // jobsMappedForScript; } ); }