diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bff742c..e8c146c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,3 +55,5 @@ stages: - build-images - build - trigger +variables: + NIX_CI_IMAGE: $CI_REGISTRY_IMAGE/nix-ci:$CI_COMMIT_SHORT_SHA diff --git a/README.md b/README.md index 1570632..c5e6b92 100644 --- a/README.md +++ b/README.md @@ -108,4 +108,4 @@ There is also `.#gitlab-ci:pipeline::job-deps:` which gener 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" diff --git a/docs/cicd_component.md b/docs/cicd_component.md index 2fe2ae2..4ea3b17 100644 --- a/docs/cicd_component.md +++ b/docs/cicd_component.md @@ -2,6 +2,13 @@ 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` - Type: `string` diff --git a/docs/index.md b/docs/index.md index 214fbf3..14f7da3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,3 +9,14 @@ 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. 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. diff --git a/flake.lock b/flake.lock index 7c47f5c..60df9c4 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1756542300, - "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=", + "lastModified": 1764667669, + "narHash": "sha256-7WUCZfmqLAssbDqwg9cUDAXrSoXN79eEEq17qhTNM/Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa", + "rev": "418468ac9527e799809c900eda37cbff999199b6", "type": "github" }, "original": { @@ -37,11 +37,11 @@ }, "locked": { "dir": "lib", - "lastModified": 1756370106, - "narHash": "sha256-l84ojcHuQWBwn4BRxQsMMfQpcq/Az/sHh/hSqFgVtyg=", + "lastModified": 1758738378, + "narHash": "sha256-NjzqdvQCDDdObEBH8x/vdhbdhrIB+N9E570uCdksGHY=", "owner": "rensa-nix", "repo": "core", - "rev": "9c1a29fa9ba7cbbb78b9e47eb8afbcd29303a3b4", + "rev": "abe19f9f13aff41de2b63304545c87d193d19ef4", "type": "gitlab" }, "original": { diff --git a/lib/VERSION b/lib/VERSION index 4a36342..ef538c2 100644 --- a/lib/VERSION +++ b/lib/VERSION @@ -1 +1 @@ -3.0.0 +3.1.2 diff --git a/lib/impl/helpers.nix b/lib/impl/helpers.nix index 48a79fc..754cead 100644 --- a/lib/impl/helpers.nix +++ b/lib/impl/helpers.nix @@ -50,7 +50,7 @@ in rec { filterJobVariables = shouldContain: job: concatMapAttrs ( name: value: - optionalAttrs ((hasInfix "/nix/store/" value) == shouldContain) { + optionalAttrs ((hasInfix builtins.storeDir value) == shouldContain) { ${name} = value; } ) diff --git a/lib/impl/jobPatched.nix b/lib/impl/jobPatched.nix index c7134fb..367a374 100644 --- a/lib/impl/jobPatched.nix +++ b/lib/impl/jobPatched.nix @@ -11,13 +11,13 @@ in pipelineName, nixConfig, }: - (builtins.removeAttrs job ["variables" "cache"]) - // (optionalAttrs nixConfig.enable ( - (prependToBeforeScript ["source setup_nix_ci \"gitlab-ci:pipeline:${pipelineName}:job-deps:${key}\""] job) + if ! nixConfig.enable + then job + else + (builtins.removeAttrs job ["variables" "cache"]) + // (prependToBeforeScript ["source setup_nix_ci \"gitlab-ci:pipeline:${pipelineName}:job-deps:${key}\""] job) // (appendToAfterScript ["finalize_nix_ci"] job) - )) - // optionalAttrs nixConfig.enable ( - (let + // (let variables = (filterJobVariables false job) // optionalAttrs nixConfig.enableRunnerCache { @@ -40,4 +40,3 @@ in optionalAttrs (cache != []) { inherit cache; }) - ) diff --git a/lib/impl/modules/job.nix b/lib/impl/modules/job.nix index 5f7d1ee..fd4fc62 100644 --- a/lib/impl/modules/job.nix +++ b/lib/impl/modules/job.nix @@ -637,22 +637,22 @@ in rec { // gitlabOptions; config = let attrsToKeep = builtins.attrNames gitlabOptions; + job = filterUnset (filterAttrs (n: _v: builtins.elem n attrsToKeep) config); in { finalConfig = cilib.mkJobPatched { key = name; - job = filterUnset (filterAttrs (n: _v: builtins.elem n attrsToKeep) config); nixConfig = config.nix; - inherit pipelineName; + inherit job pipelineName; }; depsDrv = cilib.mkJobDeps { key = name; - job = config.finalConfig; nixConfig = config.nix; + inherit job; }; runnerDrv = cilib.mkJobRun { key = name; - job = config.finalConfig; jobDeps = config.depsDrv; + inherit job; }; packages = { "gitlab-ci:pipeline:${pipelineName}:job-deps:${name}" = config.depsDrv; diff --git a/lib/impl/modules/root.nix b/lib/impl/modules/root.nix index 84fb5ba..bd99050 100644 --- a/lib/impl/modules/root.nix +++ b/lib/impl/modules/root.nix @@ -4,7 +4,7 @@ pipelineSubmodule, ... }: let - inherit (lib) mkOption types; + inherit (lib) mkOption types foldr; in rec { configSubmodule = { options = { @@ -65,7 +65,7 @@ in rec { }; }; config = { - packages = lib.fold (pipeline: acc: acc // pipeline) {} ( + packages = foldr (pipeline: acc: acc // pipeline) {} ( map (pipeline: pipeline.packages) (builtins.attrValues config.pipelines) ); soonix = config.config.soonix.finalConfig; diff --git a/lib/impl/sandbox_helper.sh b/lib/impl/sandbox_helper.sh index 294fc6b..8b76ae1 100644 --- a/lib/impl/sandbox_helper.sh +++ b/lib/impl/sandbox_helper.sh @@ -28,18 +28,19 @@ while [[ $# -gt 0 ]]; do ;; *) echo "Unknown option: $1" >&2 + echo "use --include-dirty, --no-sandbox, --keep-tmp and --keep-env " >&2 exit 1 ;; esac done -if [ "$NO_SANDBOX" = false ]; then +if [ $NO_SANDBOX = false ]; then echo "Running with simple sandboxing" - TMPDIR=$(mktemp -dt "nix-gitlab-ci.XXX") - if [ "$KEEP_TMP" = false ]; then - trap "rm -rf '$TMPDIR'" EXIT + NGCI_TMPDIR=$(mktemp -dt "nix-gitlab-ci.XXX") + if [ $KEEP_TMP = false ]; then + trap "rm -rf '$NGCI_TMPDIR'" EXIT else - echo "Temp dir will be preserved at: $TMPDIR" + echo "Temp dir will be preserved at: $NGCI_TMPDIR" fi # check if dirty @@ -50,14 +51,15 @@ if [ "$NO_SANDBOX" = false ]; then git diff --staged > "$DIRTY_PATCH" trap "rm -f '$DIRTY_PATCH'" EXIT fi - git clone . $TMPDIR - pushd $TMPDIR >/dev/null - if [[ ! -z "$DIRTY_PATCH" && "$INCLUDE_DIRTY" = true ]]; then + git clone . $NGCI_TMPDIR + pushd $NGCI_TMPDIR >/dev/null + if [[ ! -z "$DIRTY_PATCH" && $INCLUDE_DIRTY = true ]]; then echo "Copying 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 - echo "Running job in $TMPDIR" + echo "Running job in $NGCI_TMPDIR" env -i $( if [[ -n "$KEEP_ENV" ]]; then IFS=',' read -ra VARS <<< "$KEEP_ENV" diff --git a/nix/repo/ci.nix b/nix/repo/ci.nix index a76c0d8..157ede7 100644 --- a/nix/repo/ci.nix +++ b/nix/repo/ci.nix @@ -10,6 +10,7 @@ in # the child pipeline can then use the built images to test them extraData = { stages = ["build-images" "build" "trigger"]; + variables.NIX_CI_IMAGE = "$CI_REGISTRY_IMAGE/nix-ci:$CI_COMMIT_SHORT_SHA"; "build:image" = { stage = "build-images"; parallel.matrix = [ diff --git a/nix/repo/flake.lock b/nix/repo/flake.lock index 0627f8b..e5029e0 100644 --- a/nix/repo/flake.lock +++ b/nix/repo/flake.lock @@ -3,11 +3,11 @@ "devshell-lib": { "locked": { "dir": "lib", - "lastModified": 1755673398, - "narHash": "sha256-51MmR+Eo1+bKDd/Ss77wwTqi4yAR2xgmyCSEbKWSpj0=", + "lastModified": 1758204313, + "narHash": "sha256-ainbY0Oajb1HMdvy+A8QxF/P5qwcbEzJGEY5pzKdDdc=", "owner": "rensa-nix", "repo": "devshell", - "rev": "e76bef387e8a4574f9b6d37b1a424e706491af08", + "rev": "7d0c4bc78d9f017a739b0c7eb2f4e563118353e6", "type": "gitlab" }, "original": { @@ -20,11 +20,11 @@ "nixmkdocs-lib": { "locked": { "dir": "lib", - "lastModified": 1757055638, - "narHash": "sha256-KHYSkEreFe4meXzSdEbknC/HwaQSNClQkc8vzHlAsMM=", + "lastModified": 1763481845, + "narHash": "sha256-Bp0+9rDmlPWMcnKqGx+BG4+o5KO8FuDAOvXRnXrm3Fo=", "owner": "TECHNOFAB", "repo": "nixmkdocs", - "rev": "7840a5febdbeaf2da90babf6c94b3d0929d2bf74", + "rev": "73d59093df94a894d25bc4bf71880b6f00faa62f", "type": "gitlab" }, "original": { @@ -37,11 +37,11 @@ "nixtest-lib": { "locked": { "dir": "lib", - "lastModified": 1756812148, - "narHash": "sha256-0g8KNk4zoLApA51PBHOWqPLRYpprjrQuSzNCjfBQgu8=", + "lastModified": 1759340550, + "narHash": "sha256-EH9heYb/nHHzCpUGQGqVQnuyVGQ7D6MVMgJmzNvvmJ8=", "owner": "TECHNOFAB", "repo": "nixtest", - "rev": "5741109cc9ec2b6d41b56abd3f5bc51ed7a9a228", + "rev": "5a7053afcbb211b9cf8fe87f7892bb9f6b76b678", "type": "gitlab" }, "original": { @@ -63,11 +63,11 @@ "soonix-lib": { "locked": { "dir": "lib", - "lastModified": 1756797658, - "narHash": "sha256-4rkyP4oaoqG/FFVL7W8U+8hGer4tOBPff/2SeN5tJYQ=", + "lastModified": 1763323017, + "narHash": "sha256-MJyg37d+VMfRoFiVUj16FW+zkEwQXbgK9LoFF/SHoxA=", "owner": "TECHNOFAB", "repo": "soonix", - "rev": "3baef660cf8b87391d475a0455dd66fae0e60008", + "rev": "078034b01e4eaf1f9436d46721f7cbe0d96eb8b4", "type": "gitlab" }, "original": { @@ -80,11 +80,11 @@ "treefmt-nix": { "flake": false, "locked": { - "lastModified": 1756662192, - "narHash": "sha256-F1oFfV51AE259I85av+MAia221XwMHCOtZCMcZLK2Jk=", + "lastModified": 1762938485, + "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "1aabc6c05ccbcbf4a635fb7a90400e44282f61c4", + "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", "type": "github" }, "original": { diff --git a/templates/nix-gitlab-ci.yml b/templates/nix-gitlab-ci.yml index 14eeaa9..57c56d9 100644 --- a/templates/nix-gitlab-ci.yml +++ b/templates/nix-gitlab-ci.yml @@ -62,6 +62,7 @@ nix-ci:build: || NIX_CI_PIPELINE_NAME="$CI_PIPELINE_SOURCE"; fi 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 # (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 diff --git a/tests/cilib_test.nix b/tests/cilib_test.nix index 78e99a3..3b76fb8 100644 --- a/tests/cilib_test.nix +++ b/tests/cilib_test.nix @@ -64,6 +64,22 @@ 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"; expected = { @@ -129,7 +145,7 @@ # sh '' set -euo pipefail - ${ntlib.helpers.path [pkgs.jq pkgs.gnugrep pkgs.coreutils]} + ${ntlib.helpers.path (with pkgs; [jq gnugrep coreutils])} echo "two keys, one json one pretty" jq 'keys | length == 2' "${pipeline}" | grep -q true echo "key[0] is exactly 'gitlab-ci:pipeline:test'" @@ -145,10 +161,13 @@ ''; } { - name = "handle store paths in variables"; + name = "ignore store paths in variables with nix disabled"; expected = { stages = ["test"]; - test.stage = "test"; + test = { + stage = "test"; + variables."TEST" = "${pkgs.hello}"; + }; }; actual = (mkPipeline { @@ -163,6 +182,28 @@ }; }).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; + } ]; }; } diff --git a/tests/modules_test.nix b/tests/modules_test.nix index e134293..8ccbd83 100644 --- a/tests/modules_test.nix +++ b/tests/modules_test.nix @@ -77,6 +77,27 @@ 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-.*"' + ''; + } ]; }; }