nix-gitlab-ci/lib/impl/modules/pipeline.nix
Jairo Llopis ab41bc24ec
feat: auto-generate CI jobs from flake checks
Add `checks` option and `config.autoChecks` to auto-generate a CI job
for each flake check derivation. Auto-generated jobs go through
`mkJobPatched` for proper Nix setup, cache, and environment handling.

The `checks` option is automatically wired to `config.checks` in
flake-parts. System is inferred from `pkgs.system` and set via the new
`system` option. User-defined jobs with the same name always take
precedence.

New tests cover both flake-parts and direct API entry points.

MT-14138
2026-05-11 08:02:44 +01:00

252 lines
8.3 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
lib,
cilib,
jobSubmodule,
...
}: let
inherit (lib) mkOption types filterAttrs mergeAttrsList mapAttrs mkRenamedOptionModule literalExpression;
inherit (cilib.helpers) filterUnset unset mkUnsetOption toYaml toYamlPretty eitherWithSubOptions;
pipelineConfigSubmodule = {rootConfig, ...}: {
options = {
nixJobsByDefault = mkOption {
description = ''
Whether to transform all jobs to nix-configured jobs by default.
If false, you need to set `nix.enable` for each job you want to be transformed.
'';
type = types.bool;
default = rootConfig.nixJobsByDefault;
};
};
};
pipelineSubmodule = {
name,
config,
rootConfig,
rootChecks ? {},
rootAutoChecks ? {},
system,
...
}: let
#
# GITLAB OPTIONS
#
gitlabOptions = {
default = mkOption {
type = types.submodule {
# allow other keys aswell, maybe define them in the future? https://docs.gitlab.com/ci/yaml/#default
freeformType = types.anything;
options = {
image = mkUnsetOption {
type = types.str;
description = ''
Default image to use for this entire pipeline.
!!! note
Moved from top level to `default`: [GitLab Docs](https://docs.gitlab.com/ci/yaml/deprecated_keywords/).
'';
};
services = mkUnsetOption {
type = types.listOf types.anything;
description = ''
!!! note
Moved from top level to `default`: [GitLab Docs](https://docs.gitlab.com/ci/yaml/deprecated_keywords/).
'';
};
cache = mkUnsetOption {
# could be more granular
type = types.either (types.listOf types.attrs) types.attrs;
description = ''
!!! note
Moved from top level to `default`: [GitLab Docs](https://docs.gitlab.com/ci/yaml/deprecated_keywords/).
'';
};
before_script = mkUnsetOption {
type = types.listOf types.str;
description = ''
!!! note
Moved from top level to `default`: [GitLab Docs](https://docs.gitlab.com/ci/yaml/deprecated_keywords/).
'';
};
after_script = mkUnsetOption {
type = types.listOf types.str;
description = ''
!!! note
Moved from top level to `default`: [GitLab Docs](https://docs.gitlab.com/ci/yaml/deprecated_keywords/).
'';
};
};
};
# required for it to show up in the docs, but not in the config
default = unset;
defaultText = literalExpression "unset";
description = ''
Custom default values for job keywords.
'';
};
include = mkUnsetOption {
type = types.attrs;
description = ''
Import configuration from other YAML files.
[Docs](https://docs.gitlab.com/ci/yaml/#include)
'';
};
stages = mkOption {
type = types.listOf types.str;
default = [];
# .pre and .post always exist
apply = val: [".pre"] ++ val ++ [".post"];
description = ''
The names and order of the pipeline stages.
[Docs](https://docs.gitlab.com/ci/yaml/#stages)
!!! note
`.pre` and `.post` are added automatically.
'';
};
variables = mkUnsetOption {
# default/global variables can have descriptions etc.
type = types.attrsOf (eitherWithSubOptions types.str (types.submodule {
options = {
description = mkUnsetOption {
type = types.str;
description = ''
Use the `description` keyword to define a description for a default variable.
'';
};
value = mkUnsetOption {
type = types.str;
description = ''
Use the `value` keyword to define a pipeline-level (default) variables value.
'';
};
options = mkUnsetOption {
type = types.listOf types.str;
description = ''
Use `options` to define an array of values that are [selectable in the UI when running a pipeline manually](https://docs.gitlab.com/ci/pipelines/#configure-a-list-of-selectable-prefilled-variable-values).
'';
};
expand = mkUnsetOption {
type = types.bool;
description = ''
Use the `expand` keyword to configure a variable to be expandable or not.
'';
};
};
}));
description = ''
Define default CI/CD variables for all jobs in the pipeline.
Supports strings or attrs as values, for more info see [here](https://docs.gitlab.com/ci/yaml/#variablesdescription).
[Docs](https://docs.gitlab.com/ci/yaml/#default-variables)
'';
};
workflow = mkUnsetOption {
type = types.attrs;
description = ''
Control what types of pipeline run.
[Docs](https://docs.gitlab.com/ci/yaml/#workflow)
'';
};
};
in {
_file = ./pipeline.nix;
# from https://docs.gitlab.com/ci/yaml/#default
imports = builtins.map (val: mkRenamedOptionModule [val] ["default" val]) [
"after_script"
"artifacts"
"before_script"
"cache"
"hooks"
"id_tokens"
"image"
"interruptible"
"retry"
"services"
"tags"
"timeout"
];
options =
{
nix = mkOption {
description = ''
Nix-GitLab-CI config options for this pipeline.
'';
type = types.submoduleWith {
modules = [pipelineConfigSubmodule];
specialArgs.rootConfig = rootConfig;
};
default = {};
};
finalConfig = mkOption {
description = ''
Final config of the pipeline. (readonly)
'';
readOnly = true;
type = types.attrs;
};
packages = mkOption {
description = ''
Final packages for use in CI. (readonly)
'';
readOnly = true;
type = types.attrsOf types.package;
};
# jobs are nested to make distinguishing them from other keys in the ci config easier
jobs = mkOption {
description = ''
Jobs for this pipeline.
'';
type = types.attrsOf (types.submoduleWith {
modules = [jobSubmodule];
specialArgs = {
pipelineName = name;
pipelineConfig = config.nix;
};
});
default = {};
};
}
// gitlabOptions;
config = let
attrsToKeep = builtins.attrNames gitlabOptions;
autoNixConfig = {
enable = config.nix.nixJobsByDefault;
enableRunnerCache = false;
};
autoJobs = lib.optionalAttrs (rootAutoChecks.enable && name == rootAutoChecks.pipeline) (
builtins.mapAttrs (checkName: _:
cilib.mkJobPatched {
key = "auto:${checkName}";
pipelineName = name;
nixConfig = autoNixConfig;
job = {
image = "$NIX_CI_IMAGE";
stage = rootAutoChecks.stage;
script = ["nix build .#checks.\"${system}\".${checkName}"];
} // lib.optionalAttrs (rootAutoChecks.tags != []) {tags = rootAutoChecks.tags;};
}
) (
builtins.removeAttrs rootChecks (builtins.attrNames config.jobs)
)
);
in {
finalConfig =
(filterUnset (filterAttrs (n: _v: builtins.elem n attrsToKeep) config))
// mapAttrs (_name: value: value.finalConfig) config.jobs
// autoJobs;
packages =
{
"gitlab-ci:pipeline:${name}" = toYaml "gitlab-ci-config.json" config.finalConfig;
"gitlab-ci:pipeline:${name}:pretty" = toYamlPretty "gitlab-ci-config.yml" config.finalConfig;
}
// mergeAttrsList (map (job: job.packages) (builtins.attrValues config.jobs));
};
};
in {
inherit pipelineSubmodule pipelineConfigSubmodule;
}