{ lib, cilib, ... }: let inherit (lib) mkOption types filterAttrs; inherit (cilib.helpers) filterUnset mkUnsetOption eitherWithSubOptions; in rec { jobConfigSubmodule = {pipelineConfig, ...}: { options = { enable = mkOption { description = '' Transform this job to a nix-configured one. ''; type = types.bool; default = pipelineConfig.nixJobsByDefault; }; deps = mkOption { description = '' Dependencies to inject into the job before running it. ''; type = types.listOf types.package; default = []; }; enableRunnerCache = mkOption { type = types.bool; default = false; description = '' Cache this job using the GitLab Runner cache. !!! warning useful for tiny jobs, but most of the time it just takes an eternity. ''; }; runnerCacheKey = mkOption { type = types.str; default = "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"; description = '' Cache key to use for the runner nix cache. Requires [`enableRunnerCache = true`](#pipelinesnamejobsnamenixenablerunnercache). ''; }; }; }; jobSubmodule = { name, config, pipelineName, pipelineConfig, ... }: let # # GITLAB OPTIONS # gitlabOptions = { # see https://docs.gitlab.com/ci/yaml/ after_script = mkUnsetOption { type = types.listOf types.str; description = '' Override a set of commands that are executed after job. [Docs](https://docs.gitlab.com/ci/yaml/#after_script) ''; }; allow_failure = mkUnsetOption { type = eitherWithSubOptions types.bool (types.submodule { options = { exit_codes = mkUnsetOption { type = types.either types.number (types.listOf types.number); description = '' Use `allow_failure.exit_codes` to control when a job should be allowed to fail. The job is `allow_failure = true` for any of the listed exit codes, and `allow_failure = false` for any other exit code. ''; }; }; }); description = '' Allow job to fail. A failed job does not cause the pipeline to fail. [Docs](https://docs.gitlab.com/ci/yaml/#allow_failure) ''; }; artifacts = mkUnsetOption { # TODO: can be used in pipeline.default aswell type = types.submodule { options = { paths = mkUnsetOption { type = types.listOf types.str; description = '' Paths are relative to the project directory (`$CI_PROJECT_DIR`) and can’t directly link outside it. ''; }; excludes = mkUnsetOption { type = types.listOf types.str; description = '' Use `exclude` to prevent files from being added to an artifacts archive. ''; }; expire_in = mkUnsetOption { type = types.str; description = '' Use `expire_in` to specify how long [job artifacts](https://docs.gitlab.com/ci/jobs/job_artifacts/) are stored before they expire and are deleted. ''; }; expose_as = mkUnsetOption { type = types.str; description = '' Use the `expose_as` keyword to [expose artifacts in the merge request UI](https://docs.gitlab.com/ci/jobs/job_artifacts/#link-to-job-artifacts-in-the-merge-request-ui). ''; }; name = mkUnsetOption { type = types.str; description = '' Use the `name` keyword to define the name of the created artifacts archive. You can specify a unique name for every archive. ''; }; public = mkUnsetOption { type = types.bool; description = '' Use `public` to determine whether the job artifacts should be publicly available. ''; }; access = mkUnsetOption { type = types.enum [ "all" "developer" "maintainer" "none" ]; description = '' Use `access` to determine who can access the job artifacts from the GitLab UI or API. This option does not prevent you from forwarding artifacts to downstream pipelines. ''; }; reports = mkUnsetOption { type = types.attrs; description = '' Use `reports` to collect artifacts generated by included templates in jobs. ''; }; untracked = mkUnsetOption { type = types.bool; description = '' Use `untracked` to add all Git untracked files as artifacts (along with the paths defined in `paths`). `untracked` ignores configuration in the repository’s .gitignore, so matching artifacts in .gitignore are included. ''; }; when = mkUnsetOption { type = types.enum [ "on_success" "on_failure" "always" ]; description = '' Use `when` to upload artifacts on job failure or despite the failure. ''; }; }; }; description = '' List of files and directories to attach to a job on success. [Docs](https://docs.gitlab.com/ci/yaml/#artifacts) ''; }; before_script = mkUnsetOption { type = types.listOf types.str; description = '' Override a set of commands that are executed before job. [Docs](https://docs.gitlab.com/ci/yaml/#before_script) ''; }; cache = mkUnsetOption { # could be more granular type = types.either (types.listOf types.attrs) types.attrs; description = '' List of files that should be cached between subsequent runs. [Docs](https://docs.gitlab.com/ci/yaml/#cache) ''; }; coverage = mkUnsetOption { type = types.str; description = '' Code coverage settings for a given job. [Docs](https://docs.gitlab.com/ci/yaml/#coverage) ''; }; dast_configuration = mkUnsetOption { type = types.attrs; description = '' Use configuration from DAST profiles on a job level. [Docs](https://docs.gitlab.com/ci/yaml/#dast_configuration) ''; }; dependencies = mkUnsetOption { type = types.listOf types.str; description = '' Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. [Docs](https://docs.gitlab.com/ci/yaml/#dependencies) ''; }; environment = mkUnsetOption { type = eitherWithSubOptions types.str (types.submodule { options = { name = mkUnsetOption { type = types.str; example = "production"; description = '' Set a name for an environment. ''; }; url = mkUnsetOption { type = types.str; example = "https://prod.example.com"; description = '' Set a URL for an environment. ''; }; on_stop = mkUnsetOption { type = types.str; example = "down"; description = '' Closing (stopping) environments can be achieved with the `on_stop` keyword defined under `environment`. It declares a different job that runs to close the environment. ''; }; action = mkUnsetOption { type = types.enum ["start" "prepare" "stop" "verify" "access"]; description = '' Use the `action` keyword to specify how the job interacts with the environment. ''; }; auto_stop_in = mkUnsetOption { type = types.str; description = '' The `auto_stop_in` keyword specifies the lifetime of the environment. When an environment expires, GitLab automatically stops it. ''; }; kubernetes = mkUnsetOption { type = types.attrs; # no we don't go that deep :D description = '' Use the `kubernetes` keyword to configure the [dashboard for Kubernetes](https://docs.gitlab.com/ci/environments/kubernetes_dashboard/) and [GitLab-managed Kubernetes resources](https://docs.gitlab.com/user/clusters/agent/managed_kubernetes_resources/) for an environment. ''; }; deployment_tier = mkUnsetOption { type = types.enum [ "production" "staging" "testing" "development" "other" ]; description = '' Use the `deployment_tier` keyword to specify the tier of the deployment environment. ''; }; }; }); description = '' Name of an environment to which the job deploys. See the implementation for nested options, or check out the docs: [Docs](https://docs.gitlab.com/ci/yaml/#environment) ''; example = { name = "review/$CI_COMMIT_REF_SLUG"; url = "https://$CI_COMMIT_REF_SLUG.review.example.com"; action = "stop"; auto_stop_in = "1 day"; deployment_tier = "staging"; }; }; extends = mkUnsetOption { type = types.either types.str (types.listOf types.str); description = '' Configuration entries that this job inherits from. [Docs](https://docs.gitlab.com/ci/yaml/#extends) ''; }; hooks = mkUnsetOption { type = types.attrs; description = '' Use `hooks` to specify lists of commands to execute on the runner at certain stages of job execution, like before retrieving the Git repository. [Docs](https://docs.gitlab.com/ci/yaml/#hooks) ''; }; identity = mkUnsetOption { type = types.str; description = '' Authenticate with third party services using identity federation. [Docs](https://docs.gitlab.com/ci/yaml/#identity) ''; }; id_tokens = mkUnsetOption { type = types.attrs; description = '' Use `id_tokens` to create [ID tokens](https://docs.gitlab.com/ci/secrets/id_token_authentication/) to authenticate with third party services [Docs](https://docs.gitlab.com/ci/yaml/#id_tokens) ''; example = { ID_TOKEN_1.aud = "https://vault.example.com"; ID_TOKEN_2.aud = [ "https://gcp.com" "https://aws.com" ]; SIGSTORE_ID_TOKEN.aud = "sigstore"; }; }; image = mkOption { # could be more granular type = types.either types.str types.attrs; description = '' Container/OCI image to use for this job. !!! warning Setting this will mess with Nix-GitLab-CI, so be careful and only use for non-nix jobs. ''; default = "$NIX_CI_IMAGE"; example = { name = "super/sql:experimental"; entrypoint = [""]; pull_policy = "if-not-present"; docker = { platform = "arm64/v8"; user = "dave"; }; kubernetes.user = "1001"; }; }; "inherit" = mkUnsetOption { type = types.submodule { options = { default = mkUnsetOption { type = types.either types.bool (types.listOf types.str); description = '' Use `inherit.default` to control the inheritance of [default keywords](https://docs.gitlab.com/ci/yaml/#default). ''; }; variables = mkUnsetOption { type = types.either types.bool (types.listOf types.str); description = '' Use `inherit.variables` to control the inheritance of [default variables](https://docs.gitlab.com/ci/yaml/#default-variables). ''; }; }; }; description = '' Select which global defaults all jobs inherit. [Docs](https://docs.gitlab.com/ci/yaml/#inherit) ''; }; interruptible = mkUnsetOption { type = types.bool; description = '' Defines if a job can be canceled when made redundant by a newer run. [Docs](https://docs.gitlab.com/ci/yaml/#interruptible) ''; }; manual_confirmation = mkUnsetOption { type = types.str; description = '' Define a custom confirmation message for a manual job. [Docs](https://docs.gitlab.com/ci/yaml/#manual_confirmation) ''; }; needs = mkUnsetOption { # could be done more granular type = types.listOf (types.either types.str types.attrs); description = '' Execute jobs earlier than the stage ordering. [Docs](https://docs.gitlab.com/ci/yaml/#needs) ''; }; pages = mkUnsetOption { type = eitherWithSubOptions types.bool (types.submodule { options = { publish = mkUnsetOption { type = types.str; description = '' Use `pages.publish` to configure the content directory of a [`pages` job](https://docs.gitlab.com/ci/yaml/#pages). ''; }; path_prefix = mkUnsetOption { type = types.str; description = '' Use `pages.path_prefix` to configure a path prefix for [parallel deployments](https://docs.gitlab.com/user/project/pages/#parallel-deployments) of GitLab Pages. ''; }; expire_in = mkUnsetOption { type = types.str; description = '' Use `expire_in` to specify how long a deployment should be available before it expires. After the deployment is expired, it’s deactivated by a cron job running every 10 minutes. ''; }; }; }); description = '' Upload the result of a job to use with GitLab Pages. [Docs](https://docs.gitlab.com/ci/yaml/#pages) ''; }; parallel = mkUnsetOption { type = eitherWithSubOptions types.number (types.listOf (types.submodule { options = { matrix = mkUnsetOption { type = types.attrs; description = '' Use `parallel.matrix` to run a job multiple times in parallel in a single pipeline, but with different variable values for each instance of the job. ''; }; }; })); description = '' How many instances of a job should be run in parallel. [Docs](https://docs.gitlab.com/ci/yaml/#parallel) ''; example = { matrix = [ { PROVIDER = "aws"; STACK = [ "monitoring" "app1" "app2" ]; } { PROVIDER = "ovh"; STACK = ["monitoring" "backup" "app"]; } { PROVIDER = ["gcp" "vultr"]; STACK = ["data" "processing"]; } ]; }; }; release = mkUnsetOption { # could be more granular type = types.attrs; description = '' Instructs the runner to generate a [release](https://docs.gitlab.com/user/project/releases/) object. [Docs](https://docs.gitlab.com/ci/yaml/#release) ''; }; resource_group = mkUnsetOption { type = types.str; description = '' Limit job concurrency. [Docs](https://docs.gitlab.com/ci/yaml/#resource_group) ''; }; retry = mkUnsetOption { type = eitherWithSubOptions (types.ints.between 0 2) (types.submodule { options = { exit_codes = mkUnsetOption { type = types.either types.int (types.listOf types.int); description = '' Use `retry.exit_codes` with `retry.max` to retry jobs for only specific failure cases. ''; }; when = mkUnsetOption { type = types.either types.str (types.listOf types.str); description = '' Use `retry.when` with `retry.max` to retry jobs for only specific failure cases. ''; }; max = mkUnsetOption { type = types.ints.between 0 2; description = '' `retry.max` is the maximum number of retries, like retry, and can be 0, 1, or 2. ''; }; }; }); description = '' When and how many times a job can be auto-retried in case of a failure. [Docs](https://docs.gitlab.com/ci/yaml/#retry) ''; }; rules = mkUnsetOption { # could be more granular type = types.listOf types.attrs; description = '' List of conditions to evaluate and determine selected attributes of a job, and whether or not it’s created. [Docs](https://docs.gitlab.com/ci/yaml/#rules) ''; }; script = mkOption { type = types.listOf types.str; description = '' Shell script that is executed by a runner. [Docs](https://docs.gitlab.com/ci/yaml/#script) ''; }; secrets = mkUnsetOption { # could be more granular type = types.attrs; description = '' The CI/CD secrets the job needs. [Docs](https://docs.gitlab.com/ci/yaml/#secrets) ''; }; services = mkUnsetOption { # could be more granular type = types.attrs; description = '' Use Docker services images. [Docs](https://docs.gitlab.com/ci/yaml/#services) ''; }; stage = mkOption { type = types.str; description = '' Defines a job stage. [Docs](https://docs.gitlab.com/ci/yaml/#stage) ''; }; tags = mkUnsetOption { type = types.listOf types.str; description = '' List of tags that are used to select a runner. [Docs](https://docs.gitlab.com/ci/yaml/#tags) ''; }; timeout = mkUnsetOption { type = types.str; description = '' Define a custom job-level timeout that takes precedence over the project-wide setting. [Docs](https://docs.gitlab.com/ci/yaml/#timeout) ''; }; trigger = mkUnsetOption { # could be more granular type = types.either types.str types.attrs; description = '' Defines a downstream pipeline trigger. [Docs](https://docs.gitlab.com/ci/yaml/#trigger) ''; }; when = mkUnsetOption { type = types.enum ["on_success" "on_failure" "never" "always" "manual" "delayed"]; description = '' When to run job. See also [`manual_confirmation`](#pipelinesnamejobsnamemanual_confirmation) [Docs](https://docs.gitlab.com/ci/yaml/#when) ''; }; variables = mkUnsetOption { type = types.attrsOf types.str; description = '' You can use job variables in commands in the job’s `script`, `before_script`, or `after_script` sections, and also with some job keywords. Check the **Supported values** section of each job keyword to see if it supports variables. [Docs](https://docs.gitlab.com/ci/yaml/#job-variables) ''; }; }; in { options = { nix = mkOption { description = '' Nix-GitLab-CI config options for this job. ''; type = types.submoduleWith { modules = [jobConfigSubmodule]; specialArgs.pipelineConfig = pipelineConfig; }; default = {}; }; finalConfig = mkOption { description = '' Final configuration of this job. (readonly) ''; readOnly = true; internal = true; type = types.attrs; }; depsDrv = mkOption { description = '' Derivation containing all the dependencies of this job. (readonly) ''; readOnly = true; internal = true; type = types.package; }; runnerDrv = mkOption { description = '' Derivation containing the script for running the job locally. (readonly) ''; readOnly = true; internal = true; type = types.package; }; packages = mkOption { description = '' Final packages for this job, eg. for running the job or getting it's deps. (readonly) ''; readOnly = true; internal = true; type = types.attrsOf types.package; }; } // gitlabOptions; config = let attrsToKeep = builtins.attrNames gitlabOptions; job = filterUnset (filterAttrs (n: _v: builtins.elem n attrsToKeep) config); in { finalConfig = cilib.mkJobPatched { key = name; nixConfig = config.nix; inherit job pipelineName; }; depsDrv = cilib.mkJobDeps { key = name; inherit job; nixConfig = config.nix; }; runnerDrv = cilib.mkJobRun { key = name; jobDeps = config.depsDrv; inherit job; }; packages = { "gitlab-ci:pipeline:${pipelineName}:job-deps:${name}" = config.depsDrv; "gitlab-ci:pipeline:${pipelineName}:job:${name}" = config.runnerDrv; }; }; }; }