Compare commits

..

No commits in common. "main" and "v3.0.0-alpha.2" have entirely different histories.

22 changed files with 80 additions and 811 deletions

View file

@ -55,5 +55,3 @@ stages:
- build-images - build-images
- build - build
- trigger - trigger
variables:
NIX_CI_IMAGE: $CI_REGISTRY_IMAGE/nix-ci:$CI_COMMIT_SHORT_SHA

View file

@ -108,4 +108,4 @@ There is also `.#gitlab-ci:pipeline:<pipeline name>:job-deps:<name>` which gener
Some parts of this implementation are adapted/inspired from https://gitlab.com/Cynerd/gitlab-ci-nix Some parts of this implementation are adapted/inspired from https://gitlab.com/Cynerd/gitlab-ci-nix
[docs-soonix]: https://nix-gitlab-ci.projects.tf/soonix "Soonix Integration" [docs-soonix]: https://nix-gitlab-ci.projects,tf/soonix "Soonix Integration"

View file

@ -2,13 +2,6 @@
The CI/CD Component has some inputs which configure defaults for Nix GitLab CI. The CI/CD Component has some inputs which configure defaults for Nix GitLab CI.
!!! WARNING
If you get errors like `the component path is not supported` it might be related to
[this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/437996#note_1775337668).
See [here](https://gitlab.com/TECHNOFAB/nix-gitlab-ci/-/issues/27) for more.
## `version` ## `version`
- Type: `string` - Type: `string`

BIN
docs/images/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
docs/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" fill="none"><rect width="240" height="240" fill="url(#a)" rx="27"/><path fill="#E24329" d="M82.1 61.8c1 0 2 .4 2.7 1 .8.6 1.4 1.4 1.6 2.4l12.2 37.1h49.1l12.1-37.1c.3-1 .9-1.8 1.6-2.4a4.8 4.8 0 0 1 5.5-.3c.9.5 1.5 1.3 1.9 2.2l18 47 .1.4a33.4 33.4 0 0 1-11 38.5l-27.6 20.7-13.6 10.3-8.2 6.2a5.6 5.6 0 0 1-6.7 0l-8.3-6.2L98 171.3l-27.4-20.5-.2-.1A33.4 33.4 0 0 1 59.3 112l.2-.5 18-46.9a4.7 4.7 0 0 1 4.6-3Zm22.5 48c-7.5 0-11.2 3.8-11.2 11.3v21.6c0 7.5 3.7 11.3 11.2 11.3h16.6l5-3.8V144h-21c-.5 0-.7-.3-.7-.8v-22.6c0-.5.2-.7.7-.7h21v-6.2l-5-3.9h-16.6Zm29.8 0V154h11.1v-44.2h-11.1Z"/><path fill="#FC6D26" d="M187 112a33.4 33.4 0 0 1-11.1 38.6v.1l-27.6 20.6-25-18.9 2.9-2.2v-.2l8.2-6.2V154h11.1v-18.6l17-13c7.2-5.3 15.5-9 24.3-10.8l.1.5Z"/><path fill="#FCA326" d="m148.3 171.3-13.6 10.3-8.2 6.2a5.6 5.6 0 0 1-6.7 0l-8.3-6.2L98 171.3l22.9-17.3h.3l2-1.6 25 19Z"/><path fill="#FC6D26" d="M59.5 111.6c8.7 1.8 17 5.5 24.1 10.9l9.8 7.3v13c0 7.4 3.7 11.2 11.2 11.2H121l-23 17.3-27.3-20.5-.2-.1A33.4 33.4 0 0 1 59.3 112l.2-.5ZM112 144h-7c-.4 0-.6-.3-.6-.8v-5l7.6 5.8Z"/><path fill="#FC6D26" d="m209.9 159.2-12.4 22-29.5-.1 14.6 25.5-6.2 10.8h-12.8l-20.1-35 2.1-1.6 12-9 16.8-12.6H210Zm-156-50v.2l-.2.5a39.4 39.4 0 0 0 5.2 37.6L41 178.3l-12.8-21.7L43 131H13.7l-6.3-10.9 6.4-11.1 40.1.1Zm110.5 50-2.5 1.9 2.5-1.9ZM149 48l14.7-25.4h12.6l6.4 11L169 56.9c-1.5-.7-3.2-1-4.9-1h-.4c-2.2 0-4.3.9-6 2.2h-.1v.1a10.8 10.8 0 0 0-3.6 5.2l-7.7 23.8L109 22.6l25.3-.3L149 48Z"/><path fill="url(#b)" d="m209.9 159.2-12.4 22-29.5-.1 14.6 25.5-6.2 10.8h-12.8l-20.1-35 2.1-1.6 12-9 16.8-12.6H210Zm-156-50v.2l-.2.5a39.4 39.4 0 0 0 5.2 37.6L41 178.3l-12.8-21.7L43 131H13.7l-6.3-10.9 6.4-11.1 40.1.1Zm110.5 50-2.5 1.9 2.5-1.9ZM149 48l14.7-25.4h12.6l6.4 11L169 56.9c-1.5-.7-3.2-1-4.9-1h-.4c-2.2 0-4.3.9-6 2.2h-.1v.1a10.8 10.8 0 0 0-3.6 5.2l-7.7 23.8L109 22.6l25.3-.3L149 48Z"/><path fill="#E24329" d="m94.3 176 13.6 10.3 9 6.8 14 24.3-25.1.3L91 192l-14.7 25.4H63.9l-6.5-11 21-36.2-6.1-10.8 22 16.6Zm76.2-21.4-2.7 2 2.8-2Zm-106-9.6a33.3 33.3 0 0 0 .5.5l-.4-.5Zm147.2-61.6L197 109h29.3l6.3 10.9-6.4 11h-31.6c1.2-7 .5-14.2-2-20.9l-.3-.5-8.3-21.8 15-25.9 12.7 21.7Zm-25 28.2.2.5a34.5 34.5 0 0 1 1.5 18.7c1.3-6.3.8-12.7-1.5-18.7l-.1-.5-6.7-17.3 6.7 17.3Zm-89.6-53h30l12.7 22h-42L92 63.4c-.6-2.1-1.8-4-3.5-5.3l-.4-.3a10.8 10.8 0 0 0-5.3-2H82c-2 0-4 .5-5.8 1.6-2 1.2-3.5 3-4.3 5v.1l-7 18H30l12.4-21.9H72L57.3 33.3l6.2-10.8h12.8l20.8 36.2Z"/><path fill="url(#c)" d="m94.3 176 13.6 10.3 9 6.8 14 24.3-25.1.3L91 192l-14.7 25.4H63.9l-6.5-11 21-36.2-6.1-10.8 22 16.6Zm76.2-21.4-2.7 2 2.8-2Zm-106-9.6a33.3 33.3 0 0 0 .5.5l-.4-.5Zm147.2-61.6L197 109h29.3l6.3 10.9-6.4 11h-31.6c1.2-7 .5-14.2-2-20.9l-.3-.5-8.3-21.8 15-25.9 12.7 21.7Zm-25 28.2.2.5a34.5 34.5 0 0 1 1.5 18.7c1.3-6.3.8-12.7-1.5-18.7l-.1-.5-6.7-17.3 6.7 17.3Zm-89.6-53h30l12.7 22h-42L92 63.4c-.6-2.1-1.8-4-3.5-5.3l-.4-.3a10.8 10.8 0 0 0-5.3-2H82c-2 0-4 .5-5.8 1.6-2 1.2-3.5 3-4.3 5v.1l-7 18H30l12.4-21.9H72L57.3 33.3l6.2-10.8h12.8l20.8 36.2Z"/><defs><linearGradient id="b" x1="181.2" x2="141" y1="114.6" y2="45.1" gradientUnits="userSpaceOnUse"><stop stop-color="#699AD7"/><stop offset=".2" stop-color="#7EB1DD"/><stop offset="1" stop-color="#7EBAE4"/></linearGradient><linearGradient id="c" x1="151.1" x2="191.8" y1="167.7" y2="98.7" gradientUnits="userSpaceOnUse"><stop stop-color="#415E9A"/><stop offset=".2" stop-color="#4A6BAF"/><stop offset="1" stop-color="#5277C3"/></linearGradient><radialGradient id="a" cx="0" cy="0" r="1" gradientTransform="rotate(90 0 120) scale(120)" gradientUnits="userSpaceOnUse"><stop offset=".5" stop-color="#091A3D"/><stop offset="1" stop-color="#000819"/></radialGradient></defs></svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -9,14 +9,3 @@ This project provides a Nix flake module that allows you to generate your `.gitl
- **Modularity:** Define and manage your CI configurations in a structured and modular way using Nix modules, making it easier to share and reuse CI logic across multiple projects. - **Modularity:** Define and manage your CI configurations in a structured and modular way using Nix modules, making it easier to share and reuse CI logic across multiple projects.
This documentation will guide you through setting up and using Nix GitLab CI for your projects. This documentation will guide you through setting up and using Nix GitLab CI for your projects.
## Warnings
To save you from frantically searching these docs if something doesn't work as expected, here are the most important warnings ;)
!!! warning
Do not put Nix store paths into global/pipeline variables. They will simply be passed through,
resulting in bad portability (if two runners have different archs for example, one cannot find the path).
If you need any Nix store path in env variables, always do it on the job level, there
it will automatically be computed at runtime, thus will always work no matter which runner it runs on.

View file

@ -1,15 +0,0 @@
.md-header__button.md-logo {
margin: 0;
padding-top: .2rem;
padding-bottom: .2rem;
}
[dir="ltr"] .md-header__title {
margin-left: 0;
}
.md-header__button.md-logo img,
.md-header__button.md-logo svg {
height: 2rem;
}

12
flake.lock generated
View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1764667669, "lastModified": 1756542300,
"narHash": "sha256-7WUCZfmqLAssbDqwg9cUDAXrSoXN79eEEq17qhTNM/Y=", "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "418468ac9527e799809c900eda37cbff999199b6", "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -37,11 +37,11 @@
}, },
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1758738378, "lastModified": 1756370106,
"narHash": "sha256-NjzqdvQCDDdObEBH8x/vdhbdhrIB+N9E570uCdksGHY=", "narHash": "sha256-l84ojcHuQWBwn4BRxQsMMfQpcq/Az/sHh/hSqFgVtyg=",
"owner": "rensa-nix", "owner": "rensa-nix",
"repo": "core", "repo": "core",
"rev": "abe19f9f13aff41de2b63304545c87d193d19ef4", "rev": "9c1a29fa9ba7cbbb78b9e47eb8afbcd29303a3b4",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {

View file

@ -1 +1 @@
3.1.2 3.0.0-alpha.2

View file

@ -50,7 +50,7 @@ in rec {
filterJobVariables = shouldContain: job: filterJobVariables = shouldContain: job:
concatMapAttrs ( concatMapAttrs (
name: value: name: value:
optionalAttrs ((hasInfix builtins.storeDir value) == shouldContain) { optionalAttrs ((hasInfix "/nix/store/" value) == shouldContain) {
${name} = value; ${name} = value;
} }
) )
@ -84,7 +84,7 @@ in rec {
unsetOr = typ: unsetOr = typ:
(types.either unsetType typ) (types.either unsetType typ)
// { // {
inherit (typ) description getSubOptions; inherit (typ) description;
}; };
mkUnsetOption = opts: mkUnsetOption = opts:
mkOption (opts mkOption (opts
@ -93,14 +93,6 @@ in rec {
default = opts.default or unset; default = opts.default or unset;
defaultText = literalExpression "unset"; defaultText = literalExpression "unset";
}); });
eitherWithSubOptions = typ_one: typ_two:
(types.either typ_one typ_two)
// {
getSubOptions =
if (typ_one.getSubOptions "test" != {})
then typ_one.getSubOptions
else typ_two.getSubOptions;
};
filterUnset = value: filterUnset = value:
if builtins.isAttrs value && !builtins.hasAttr "_type" value if builtins.isAttrs value && !builtins.hasAttr "_type" value

View file

@ -11,13 +11,13 @@ in
pipelineName, pipelineName,
nixConfig, nixConfig,
}: }:
if ! nixConfig.enable (builtins.removeAttrs job ["variables" "cache"])
then job // (optionalAttrs nixConfig.enable (
else (prependToBeforeScript ["source setup_nix_ci \"gitlab-ci:pipeline:${pipelineName}:job-deps:${key}\""] job)
(builtins.removeAttrs job ["variables" "cache"])
// (prependToBeforeScript ["source setup_nix_ci \"gitlab-ci:pipeline:${pipelineName}:job-deps:${key}\""] job)
// (appendToAfterScript ["finalize_nix_ci"] job) // (appendToAfterScript ["finalize_nix_ci"] job)
// (let ))
// optionalAttrs nixConfig.enable (
(let
variables = variables =
(filterJobVariables false job) (filterJobVariables false job)
// optionalAttrs nixConfig.enableRunnerCache { // optionalAttrs nixConfig.enableRunnerCache {
@ -40,3 +40,4 @@ in
optionalAttrs (cache != []) { optionalAttrs (cache != []) {
inherit cache; inherit cache;
}) })
)

View file

@ -4,7 +4,7 @@
... ...
}: let }: let
inherit (lib) mkOption types filterAttrs; inherit (lib) mkOption types filterAttrs;
inherit (cilib.helpers) filterUnset mkUnsetOption eitherWithSubOptions; inherit (cilib.helpers) filterUnset mkUnsetOption;
in rec { in rec {
jobConfigSubmodule = {pipelineConfig, ...}: { jobConfigSubmodule = {pipelineConfig, ...}: {
options = { options = {
@ -53,539 +53,43 @@ in rec {
# GITLAB OPTIONS # GITLAB OPTIONS
# #
gitlabOptions = { gitlabOptions = {
# see https://docs.gitlab.com/ci/yaml/ stage = mkOption {
after_script = mkUnsetOption {
type = types.listOf types.str;
description = '' description = ''
Override a set of commands that are executed after job. Pipeline stage to run this job in.
[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 cant 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 repositorys .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; 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 { image = mkOption {
# could be more granular
type = types.either types.str types.attrs;
description = '' description = ''
Container/OCI image to use for this job. Container/OCI image to use for this job.
!!! warning !!! warning
Setting this will mess with Nix-GitLab-CI, so be careful and only use for non-nix jobs. Setting this will mess with Nix-GitLab-CI, so be careful and only use for non-nix jobs.
''; '';
type = types.str;
default = "$NIX_CI_IMAGE"; 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, its 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 its 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 { variables = mkUnsetOption {
type = types.attrsOf types.str; type = types.attrsOf types.str;
description = '' };
You can use job variables in commands in the jobs `script`, `before_script`, or `after_script` sections, and also with some job keywords. before_script = mkUnsetOption {
Check the **Supported values** section of each job keyword to see if it supports variables. type = types.listOf types.str;
};
[Docs](https://docs.gitlab.com/ci/yaml/#job-variables) script = mkOption {
''; type = types.listOf types.str;
};
after_script = mkUnsetOption {
type = types.listOf types.str;
};
artifacts = mkUnsetOption {
type = types.attrs; # TODO: more granular
description = '''';
};
rules = mkUnsetOption {
type = types.listOf types.attrs;
};
allow_failure = mkUnsetOption {
type = types.bool;
}; };
}; };
in { in {
@ -637,22 +141,22 @@ in rec {
// gitlabOptions; // gitlabOptions;
config = let config = let
attrsToKeep = builtins.attrNames gitlabOptions; attrsToKeep = builtins.attrNames gitlabOptions;
job = filterUnset (filterAttrs (n: _v: builtins.elem n attrsToKeep) config);
in { in {
finalConfig = cilib.mkJobPatched { finalConfig = cilib.mkJobPatched {
key = name; key = name;
job = filterUnset (filterAttrs (n: _v: builtins.elem n attrsToKeep) config);
nixConfig = config.nix; nixConfig = config.nix;
inherit job pipelineName; inherit pipelineName;
}; };
depsDrv = cilib.mkJobDeps { depsDrv = cilib.mkJobDeps {
key = name; key = name;
job = config.finalConfig;
nixConfig = config.nix; nixConfig = config.nix;
inherit job;
}; };
runnerDrv = cilib.mkJobRun { runnerDrv = cilib.mkJobRun {
key = name; key = name;
job = config.finalConfig;
jobDeps = config.depsDrv; jobDeps = config.depsDrv;
inherit job;
}; };
packages = { packages = {
"gitlab-ci:pipeline:${pipelineName}:job-deps:${name}" = config.depsDrv; "gitlab-ci:pipeline:${pipelineName}:job-deps:${name}" = config.depsDrv;

View file

@ -4,8 +4,8 @@
jobSubmodule, jobSubmodule,
... ...
}: let }: let
inherit (lib) mkOption types filterAttrs mergeAttrsList mapAttrs mkRenamedOptionModule literalExpression; inherit (lib) mkOption types filterAttrs mergeAttrsList mapAttrs;
inherit (cilib.helpers) filterUnset unset mkUnsetOption toYaml toYamlPretty eitherWithSubOptions; inherit (cilib.helpers) filterUnset mkUnsetOption toYaml toYamlPretty;
pipelineConfigSubmodule = {rootConfig, ...}: { pipelineConfigSubmodule = {rootConfig, ...}: {
options = { options = {
@ -30,143 +30,19 @@
# GITLAB OPTIONS # GITLAB OPTIONS
# #
gitlabOptions = { 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 { stages = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [];
# .pre and .post always exist # .pre and .post always exist
apply = val: [".pre"] ++ val ++ [".post"]; 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 { variables = mkUnsetOption {
# default/global variables can have descriptions etc. type = types.attrsOf types.str;
type = types.attrsOf (eitherWithSubOptions types.str (types.submodule { description = '''';
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 { in {
_file = ./pipeline.nix; _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 = options =
{ {
nix = mkOption { nix = mkOption {

View file

@ -4,7 +4,7 @@
pipelineSubmodule, pipelineSubmodule,
... ...
}: let }: let
inherit (lib) mkOption types foldr; inherit (lib) mkOption types;
in rec { in rec {
configSubmodule = { configSubmodule = {
options = { options = {
@ -65,7 +65,7 @@ in rec {
}; };
}; };
config = { config = {
packages = foldr (pipeline: acc: acc // pipeline) {} ( packages = lib.fold (pipeline: acc: acc // pipeline) {} (
map (pipeline: pipeline.packages) (builtins.attrValues config.pipelines) map (pipeline: pipeline.packages) (builtins.attrValues config.pipelines)
); );
soonix = config.config.soonix.finalConfig; soonix = config.config.soonix.finalConfig;

View file

@ -28,19 +28,18 @@ while [[ $# -gt 0 ]]; do
;; ;;
*) *)
echo "Unknown option: $1" >&2 echo "Unknown option: $1" >&2
echo "use --include-dirty, --no-sandbox, --keep-tmp and --keep-env <ENV>" >&2
exit 1 exit 1
;; ;;
esac esac
done done
if [ $NO_SANDBOX = false ]; then if [ "$NO_SANDBOX" = false ]; then
echo "Running with simple sandboxing" echo "Running with simple sandboxing"
NGCI_TMPDIR=$(mktemp -dt "nix-gitlab-ci.XXX") TMPDIR=$(mktemp -dt "nix-gitlab-ci.XXX")
if [ $KEEP_TMP = false ]; then if [ "$KEEP_TMP" = false ]; then
trap "rm -rf '$NGCI_TMPDIR'" EXIT trap "rm -rf '$TMPDIR'" EXIT
else else
echo "Temp dir will be preserved at: $NGCI_TMPDIR" echo "Temp dir will be preserved at: $TMPDIR"
fi fi
# check if dirty # check if dirty
@ -51,15 +50,14 @@ if [ $NO_SANDBOX = false ]; then
git diff --staged > "$DIRTY_PATCH" git diff --staged > "$DIRTY_PATCH"
trap "rm -f '$DIRTY_PATCH'" EXIT trap "rm -f '$DIRTY_PATCH'" EXIT
fi fi
git clone . $NGCI_TMPDIR git clone . $TMPDIR
pushd $NGCI_TMPDIR >/dev/null pushd $TMPDIR >/dev/null
if [[ ! -z "$DIRTY_PATCH" && $INCLUDE_DIRTY = true ]]; then if [[ ! -z "$DIRTY_PATCH" && "$INCLUDE_DIRTY" = true ]]; then
echo "Copying dirty changes..." echo "Copying dirty changes..."
git apply "$DIRTY_PATCH" 2>/dev/null || echo "Failed to copy dirty changes" git apply "$DIRTY_PATCH" 2>/dev/null || echo "Failed to copy dirty changes"
git add . # required so the files are staged again
fi fi
echo "Running job in $NGCI_TMPDIR" echo "Running job in $TMPDIR"
env -i $( env -i $(
if [[ -n "$KEEP_ENV" ]]; then if [[ -n "$KEEP_ENV" ]]; then
IFS=',' read -ra VARS <<< "$KEEP_ENV" IFS=',' read -ra VARS <<< "$KEEP_ENV"

View file

@ -1,5 +1,5 @@
{inputs, ...}: let {inputs, ...}: let
inherit (inputs) cilib; inherit (inputs) pkgs cilib;
in in
cilib.mkCI { cilib.mkCI {
config.soonix = { config.soonix = {
@ -10,7 +10,6 @@ in
# the child pipeline can then use the built images to test them # the child pipeline can then use the built images to test them
extraData = { extraData = {
stages = ["build-images" "build" "trigger"]; stages = ["build-images" "build" "trigger"];
variables.NIX_CI_IMAGE = "$CI_REGISTRY_IMAGE/nix-ci:$CI_COMMIT_SHORT_SHA";
"build:image" = { "build:image" = {
stage = "build-images"; stage = "build-images";
parallel.matrix = [ parallel.matrix = [

View file

@ -38,14 +38,12 @@ in
}; };
config = { config = {
site_name = "Nix-GitLab-CI"; site_name = "Nix-GitLab-CI";
site_url = "https://nix-gitlab-ci.projects.tf";
repo_name = "TECHNOFAB/nix-gitlab-ci"; repo_name = "TECHNOFAB/nix-gitlab-ci";
repo_url = "https://gitlab.com/TECHNOFAB/nix-gitlab-ci"; repo_url = "https://gitlab.com/TECHNOFAB/nix-gitlab-ci";
extra_css = ["style.css"];
theme = { theme = {
logo = "images/logo.svg"; logo = "images/logo.png";
icon.repo = "simple/gitlab"; icon.repo = "simple/gitlab";
favicon = "images/logo.svg"; favicon = "images/favicon.png";
}; };
nav = [ nav = [
{"Introduction" = "index.md";} {"Introduction" = "index.md";}

30
nix/repo/flake.lock generated
View file

@ -3,11 +3,11 @@
"devshell-lib": { "devshell-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1758204313, "lastModified": 1755673398,
"narHash": "sha256-ainbY0Oajb1HMdvy+A8QxF/P5qwcbEzJGEY5pzKdDdc=", "narHash": "sha256-51MmR+Eo1+bKDd/Ss77wwTqi4yAR2xgmyCSEbKWSpj0=",
"owner": "rensa-nix", "owner": "rensa-nix",
"repo": "devshell", "repo": "devshell",
"rev": "7d0c4bc78d9f017a739b0c7eb2f4e563118353e6", "rev": "e76bef387e8a4574f9b6d37b1a424e706491af08",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@ -20,11 +20,11 @@
"nixmkdocs-lib": { "nixmkdocs-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1763481845, "lastModified": 1755785622,
"narHash": "sha256-Bp0+9rDmlPWMcnKqGx+BG4+o5KO8FuDAOvXRnXrm3Fo=", "narHash": "sha256-xBb9PCkszmrWSEqUiPC7oBJABm1thF572S5QHtloZ+M=",
"owner": "TECHNOFAB", "owner": "TECHNOFAB",
"repo": "nixmkdocs", "repo": "nixmkdocs",
"rev": "73d59093df94a894d25bc4bf71880b6f00faa62f", "rev": "61da605a9bff12f66c4b743f43aea59ca200f533",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@ -37,11 +37,11 @@
"nixtest-lib": { "nixtest-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1759340550, "lastModified": 1756812148,
"narHash": "sha256-EH9heYb/nHHzCpUGQGqVQnuyVGQ7D6MVMgJmzNvvmJ8=", "narHash": "sha256-0g8KNk4zoLApA51PBHOWqPLRYpprjrQuSzNCjfBQgu8=",
"owner": "TECHNOFAB", "owner": "TECHNOFAB",
"repo": "nixtest", "repo": "nixtest",
"rev": "5a7053afcbb211b9cf8fe87f7892bb9f6b76b678", "rev": "5741109cc9ec2b6d41b56abd3f5bc51ed7a9a228",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@ -63,11 +63,11 @@
"soonix-lib": { "soonix-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1763323017, "lastModified": 1756797658,
"narHash": "sha256-MJyg37d+VMfRoFiVUj16FW+zkEwQXbgK9LoFF/SHoxA=", "narHash": "sha256-4rkyP4oaoqG/FFVL7W8U+8hGer4tOBPff/2SeN5tJYQ=",
"owner": "TECHNOFAB", "owner": "TECHNOFAB",
"repo": "soonix", "repo": "soonix",
"rev": "078034b01e4eaf1f9436d46721f7cbe0d96eb8b4", "rev": "3baef660cf8b87391d475a0455dd66fae0e60008",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@ -80,11 +80,11 @@
"treefmt-nix": { "treefmt-nix": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1762938485, "lastModified": 1756662192,
"narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", "narHash": "sha256-F1oFfV51AE259I85av+MAia221XwMHCOtZCMcZLK2Jk=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", "rev": "1aabc6c05ccbcbf4a635fb7a90400e44282f61c4",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -62,7 +62,6 @@ nix-ci:build:
|| NIX_CI_PIPELINE_NAME="$CI_PIPELINE_SOURCE"; || NIX_CI_PIPELINE_NAME="$CI_PIPELINE_SOURCE";
fi fi
echo "NIX_CI_GENERATED_PIPELINE_NAME=$NIX_CI_PIPELINE_NAME" >> trigger.env echo "NIX_CI_GENERATED_PIPELINE_NAME=$NIX_CI_PIPELINE_NAME" >> trigger.env
echo "ORIGINAL_CI_PIPELINE_SOURCE=$CI_PIPELINE_SOURCE" >> trigger.env
# inheritance of pipeline variables is a bit weird, so explicitly override them # inheritance of pipeline variables is a bit weird, so explicitly override them
# (ctx: setting any of these in the project variables would only apply correctly # (ctx: setting any of these in the project variables would only apply correctly
# in this pipeline, not the child pipeline, instead weirdly enough the default # in this pipeline, not the child pipeline, instead weirdly enough the default

View file

@ -64,22 +64,6 @@
nixConfig.enable = false; nixConfig.enable = false;
}; };
} }
{
name = "jobPatched nix disabled with variables and cache";
expected = {
variables."HELLO" = "world";
cache = [{key = "example";}];
};
actual = mkJobPatched {
key = "test";
pipelineName = "test";
job = {
variables."HELLO" = "world";
cache = [{key = "example";}];
};
nixConfig.enable = false;
};
}
{ {
name = "jobPatched without runner cache"; name = "jobPatched without runner cache";
expected = { expected = {
@ -145,7 +129,7 @@
# sh # sh
'' ''
set -euo pipefail set -euo pipefail
${ntlib.helpers.path (with pkgs; [jq gnugrep coreutils])} ${ntlib.helpers.path [pkgs.jq pkgs.gnugrep pkgs.coreutils]}
echo "two keys, one json one pretty" echo "two keys, one json one pretty"
jq 'keys | length == 2' "${pipeline}" | grep -q true jq 'keys | length == 2' "${pipeline}" | grep -q true
echo "key[0] is exactly 'gitlab-ci:pipeline:test'" echo "key[0] is exactly 'gitlab-ci:pipeline:test'"
@ -161,13 +145,10 @@
''; '';
} }
{ {
name = "ignore store paths in variables with nix disabled"; name = "handle store paths in variables";
expected = { expected = {
stages = ["test"]; stages = ["test"];
test = { test.stage = "test";
stage = "test";
variables."TEST" = "${pkgs.hello}";
};
}; };
actual = actual =
(mkPipeline { (mkPipeline {
@ -182,28 +163,6 @@
}; };
}).finalConfig; }).finalConfig;
} }
{
# it doesn't make much sense to have any nix store path in variables, but we ignore it for global variables
name = "ignore store paths in global variables";
expected = {
variables = {
HELLO = "world";
CURL = toString pkgs.curl;
};
};
actual =
(mkPipeline {
name = "test";
nixConfig.enable = true;
pipeline = {
variables = {
HELLO = "world";
CURL = toString pkgs.curl;
};
jobs = {};
};
}).finalConfig;
}
]; ];
}; };
} }

View file

@ -77,27 +77,6 @@
assert_file_contains ${package} '"EXAMPLE":"/nix/store/.*-hello-.*"' assert_file_contains ${package} '"EXAMPLE":"/nix/store/.*-hello-.*"'
''; '';
} }
{
name = "correctly inject variables containing nix store paths at runtime";
type = "script";
script = let
package =
(cilib.mkCI {
pipelines."test".jobs."test" = {
stage = ".pre";
variables.EXAMPLE = "${pkgs.hello}";
script = [];
};
}).packages."gitlab-ci:pipeline:test:job-deps:test";
in
# sh
''
${ntlib.helpers.path [pkgs.gnugrep]}
${ntlib.helpers.scriptHelpers}
assert_file_contains ${package} 'export PATH=":$PATH";'
assert_file_contains ${package} 'export EXAMPLE="/nix/store/.*-hello-.*"'
'';
}
]; ];
}; };
} }