diff --git a/.envrc b/.envrc index 565a52a..f990172 100644 --- a/.envrc +++ b/.envrc @@ -1,2 +1 @@ -source $(fetchurl https://gitlab.com/rensa-nix/direnv/-/raw/v0.3.0/direnvrc "sha256-u7+KEz684NnIZ+Vh5x5qLrt8rKdnUNexewBoeTcEVHQ=") -use ren //repo/devShells/default +use flake . --impure --accept-flake-config diff --git a/.gitignore b/.gitignore index fe07db9..bbee8ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ -.ren/ +.idea +.devenv +.direnv +.pre-commit-config.yaml result +*.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bff742c..24b9507 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,57 +1,49 @@ -# Generated by soonix, DO NOT EDIT +include: + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/nix-gitlab-ci@$CI_COMMIT_SHA + inputs: + version: $CI_COMMIT_SHORT_SHA +stages: + - build-images + - build + - trigger build:image: - after_script: - - install -D result dist/nix-ci-$ARCH.tar.gz - artifacts: - paths: - - dist - image: nixpkgs/nix-flakes:latest + stage: build-images parallel: matrix: - - ARCH: - - x86_64-linux - - aarch64-linux + - ARCH: ["x86_64-linux", "aarch64-linux"] + image: nixpkgs/nix-flakes:latest script: - - nix build .#image --system $ARCH - stage: build-images + - nix build .#image --system $ARCH + after_script: + - install -D result dist/nix-ci-$ARCH.tar.gz + artifacts: + paths: + - dist deploy:image: - before_script: - - 'nix profile install nixpkgs#buildah - - export PATH="$PATH:$HOME/.nix-profile/bin" - - export REGISTRY_AUTH_FILE=${HOME}/auth.json - - echo "$CI_REGISTRY_PASSWORD" | buildah login -u "$CI_REGISTRY_USER" --password-stdin - $CI_REGISTRY - - mkdir -p /etc/containers && echo ''{"default":[{"type":"insecureAcceptAnything"}]}'' - > /etc/containers/policy.json - - mkdir -p /var/tmp - - ' + stage: build-images image: nixpkgs/nix-flakes:latest needs: - - build:image + - build:image + before_script: + - nix profile install nixpkgs#buildah + - export PATH="$PATH:$HOME/.nix-profile/bin" + - export REGISTRY_AUTH_FILE=''${HOME}/auth.json + - echo "$CI_REGISTRY_PASSWORD" | buildah login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY + - mkdir -p /etc/containers && echo '{"default":[{"type":"insecureAcceptAnything"}]}' > /etc/containers/policy.json + - mkdir -p /var/tmp script: - - "export NORMALIZED_BRANCH=${CI_COMMIT_BRANCH/\\//-}\nbuildah manifest create localhost/nix-ci\n\ - buildah manifest add localhost/nix-ci docker-archive:dist/nix-ci-x86_64-linux.tar.gz\n\ - buildah manifest add localhost/nix-ci docker-archive:dist/nix-ci-aarch64-linux.tar.gz\n\ - buildah manifest push --all localhost/nix-ci docker://${CI_REGISTRY_IMAGE}/nix-ci:${CI_COMMIT_SHORT_SHA}\n\ - # branches\nif [ -z \"$CI_COMMIT_TAG\" ]; then\n buildah manifest push --all\ - \ localhost/nix-ci docker://${CI_REGISTRY_IMAGE}/nix-ci:${NORMALIZED_BRANCH/main/latest}\n\ - fi\n# tags\nif [ -n \"$CI_COMMIT_TAG\" ]; then\n buildah manifest push --all\ - \ localhost/nix-ci docker://${CI_REGISTRY_IMAGE}/nix-ci:${CI_COMMIT_TAG}\nfi\n" - stage: build-images -include: -- component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/nix-gitlab-ci@$CI_COMMIT_SHORT_SHA - inputs: - cache_files: - - flake.* - - nix/repo/ci.nix - version: $CI_COMMIT_SHORT_SHA -stages: -- build-images -- build -- trigger + - export NORMALIZED_BRANCH=${CI_COMMIT_BRANCH/\//-} + - buildah manifest create localhost/nix-ci + - buildah manifest add localhost/nix-ci docker-archive:dist/nix-ci-x86_64-linux.tar.gz + - buildah manifest add localhost/nix-ci docker-archive:dist/nix-ci-aarch64-linux.tar.gz + - buildah manifest push --all localhost/nix-ci docker://''${CI_REGISTRY_IMAGE}/nix-ci:${CI_COMMIT_SHORT_SHA} + # branches + - | + if [ -z "$CI_COMMIT_TAG" ]; then + buildah manifest push --all localhost/nix-ci docker://''${CI_REGISTRY_IMAGE}/nix-ci:${NORMALIZED_BRANCH/main/latest} + fi + # tags + - | + if [ -n "$CI_COMMIT_TAG" ]; then + buildah manifest push --all localhost/nix-ci docker://''${CI_REGISTRY_IMAGE}/nix-ci:${CI_COMMIT_TAG} + fi diff --git a/README.md b/README.md index 1570632..ceaca42 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This allows easily using any Nix package in CI. Also makes it possible to split CI parts in a separate module which can be imported in multiple projects. -## Usage (with flake-parts) +## Usage ```nix # flake.nix @@ -28,27 +28,23 @@ Also makes it possible to split CI parts in a separate module which can be impor ... perSystem = {pkgs, ...}: { + # ci is a shortcut and creates a "default" pipeline ci = { - config = { - # configure Nix-GitLab-CI here, see docs for options - }; - pipelines."default" = { - stages = ["test"]; - jobs = { - "test" = { - stage = "test"; - nix.deps = [pkgs.unixtools.ping]; - script = [ - "ping -c 5 8.8.8.8" - ]; - }; + stages = ["test"]; + jobs = { + "test" = { + stage = "test"; + nix.deps = [pkgs.unixtools.ping]; + script = [ + "ping -c 5 8.8.8.8" + ]; }; }; - # runs on a merge request for example - pipelines."merge_request_event" = { - stages = ["some_stage"]; - jobs = { ... }; - }; + }; + # runs on a merge request for example + pipelines."merge_request_event" = { + stages = ["some_stage"]; + jobs = { ... }; }; ... } @@ -56,9 +52,6 @@ Also makes it possible to split CI parts in a separate module which can be impor } ``` -Now either use this in your .gitlab-ci.yml or setup Soonix to auto generate this -file for you with the right version (see the [docs][docs-soonix] for more). - ```yaml # .gitlab-ci.yml include: @@ -67,19 +60,6 @@ include: version: # docker image tag, use the same version as a above ``` -## Usage (directly) - -```nix -let - cilib = inputs.nix-gitlab-ci.lib {inherit pkgs;}; -in - cilib.mkCI { - config = ...; - pipelines."default" = ...; - }; - # exposes `soonix` for the soonix hook and `packages` which contain the configs, jobs etc. -``` - ## Utilities ### Disable Caching temporarily @@ -107,5 +87,3 @@ There is also `.#gitlab-ci:pipeline::job-deps:` which gener ## Thanks to 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" diff --git a/docs/examples.md b/docs/examples.md index 336a55a..6f82a63 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -1,11 +1,16 @@ # Example Configs +## V2 + - [TECHNOFAB/nix-gitlab-ci](https://gitlab.com/TECHNOFAB/nix-gitlab-ci) + See `flake.nix` for some random example jobs. - [TECHNOFAB/nixlets](https://gitlab.com/TECHNOFAB/nixlets) -- [TECHNOFAB/nixible](https://gitlab.com/TECHNOFAB/nixible) - [TECHNOFAB/nixmkdocs](https://gitlab.com/TECHNOFAB/nixmkdocs) - [TECHNOFAB/tofunix](https://gitlab.com/TECHNOFAB/tofunix) -- [TECHNOFAB/nixtest](https://gitlab.com/TECHNOFAB/nixtest) + +## Old / V1 + +- [TECHNOFAB/coder-templates](https://gitlab.com/TECHNOFAB/coder-templates) !!! note diff --git a/docs/images/favicon.png b/docs/images/favicon.png new file mode 100644 index 0000000..135b030 Binary files /dev/null and b/docs/images/favicon.png differ diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 0000000..2a70bf6 Binary files /dev/null and b/docs/images/logo.png differ diff --git a/docs/images/logo.svg b/docs/images/logo.svg deleted file mode 100755 index d8dce95..0000000 --- a/docs/images/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/options.md b/docs/options.md deleted file mode 100644 index 4ca74a4..0000000 --- a/docs/options.md +++ /dev/null @@ -1,3 +0,0 @@ -# Options - -{% include 'options.md' %} diff --git a/docs/setup.md b/docs/setup.md index c1f85df..d3aa96e 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -36,7 +36,8 @@ flake module within your `flake-parts` configuration. perSystem = { pkgs, ... }: { # define your CI pipelines - # ci.pipelines."merge_request_event" = { ... }; + # ci = { ... }; + # pipelines."merge_request_event" = { ... }; }; }; } @@ -72,10 +73,3 @@ include: Again, ensure `` matches the version used in your `flake.nix`. This component includes a job (`build:nix-ci`) that will evaluate your Nix configuration and generate the `.gitlab-ci.yml` used for the pipeline run. - -!!! note - - Since V3 [Soonix](https://soonix.projects.tf) is supported, this can - automatically generate the `.gitlab-ci.yml` for you, with the version - automatically following the flake. - See [Soonix Integration](./soonix.md) for more. diff --git a/docs/soonix.md b/docs/soonix.md deleted file mode 100644 index b333ab6..0000000 --- a/docs/soonix.md +++ /dev/null @@ -1,20 +0,0 @@ -# Soonix Integration - -[Soonix](https://soonix.projects.tf) can be used to automatically generate the -`.gitlab-ci.yml` for you. -This will by default include the CI/CD component with the same version as the -flake (using the `VERSION` file in this repo). - -You can specify some options to configure this, like changing the component URL -or adding extra data to your `.gitlab-ci.yml` (like this repo does to bootstrap -Nix-GitLab-CI). See [Options](./options.md#configsoonix) for all the config options. - -You can use it like this: - -```nix -let - ci = cilib.mkCI { ... }; -in { - soonix.hooks."ci" = ci.soonix; -} -``` diff --git a/docs/style.css b/docs/style.css deleted file mode 100644 index b2ae4ff..0000000 --- a/docs/style.css +++ /dev/null @@ -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; -} - diff --git a/docs/usage.md b/docs/usage.md index 44c0bf7..b28a755 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,73 +1,33 @@ # Usage -## Usage (with flake-parts) +To create a basic pipeline, configure it by setting `ci` in `perSystem`. +The schema is similar to the `.gitlab-ci.yml`, only jobs are defined differently: ```nix -# flake.nix -{ - ... - inputs.nix-gitlab-ci.url = "gitlab:TECHNOFAB/nix-gitlab-ci/?dir=lib"; # recommendation: pin to the latest release/version - - outputs = {...}: flake-parts.lib.mkFlake {...} { - imports = [ - inputs.nix-gitlab-ci.flakeModule - ]; - ... - - perSystem = {pkgs, ...}: { - ci = { - config = { - # configure Nix-GitLab-CI here, see docs for options - }; - pipelines."default" = { - stages = ["test"]; - jobs = { - "test" = { - stage = "test"; - nix.deps = [pkgs.unixtools.ping]; - script = [ - "ping -c 5 8.8.8.8" - ]; - }; - }; - }; - # runs on a merge request for example - pipelines."merge_request_event" = { - stages = ["some_stage"]; - jobs = { ... }; - }; - }; - ... - } - } -} -``` - -Now either use this in your .gitlab-ci.yml or setup Soonix to auto generate this -file for you with the right version (see the [docs][docs-soonix] for more). - -```yaml -# .gitlab-ci.yml -include: - - component: gitlab.com/TECHNOFAB/nix-gitlab-ci/nix-gitlab-ci@ # recommendation: pin to the latest release/version (don't use "main" etc.) - inputs: - version: # docker image tag, use the same version as a above -``` - -## Usage (directly) - -```nix -let - cilib = inputs.nix-gitlab-ci.lib {inherit pkgs;}; -in - cilib.mkCI { - config = ...; - pipelines."default" = ...; +ci = { + # Nix GitLab CI specific config, see `configType` in `flakeModule.nix` + config = {}; + jobs = { + "job-a" = {}; + "job-b" = {}; }; - # exposes `soonix` for the soonix hook and `packages` which contain the configs, jobs etc. +}; ``` -______________________________________________________________________ +For every job, there are a couple of settings you can adjust aswell: + +```nix +"job-a" = { + # see `jobType` in `flakeModule.nix` + nix = { + enable = true; # is this a nix-based job? + deps = []; # dependencies to install for this job + # for gitlab runner cache: + enable-runner-cache = false; + runner-cache-key = ""; + }; +}; +``` Since V2 multiple pipelines are supported. See [Multiple Pipelines](./multi_pipeline.md) for more. diff --git a/docs/utilities.md b/docs/utilities.md index b82091d..e5a7b96 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -57,8 +57,3 @@ correctly resolved across different architectures). You can use this to inspect the environment that would be set up for a job without running the full script. - -## Viewing generated config in YAML - -Since the GitLab CI config is generated simply using JSON, it's hard to read and -debug. For debugging V3 now adds another package `gitlab-ci:pipeline::pretty`. diff --git a/examples/flake-parts/flake.lock b/examples/flake-parts/flake.lock deleted file mode 100644 index 2e81bcf..0000000 --- a/examples/flake-parts/flake.lock +++ /dev/null @@ -1,95 +0,0 @@ -{ - "nodes": { - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1756770412, - "narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "4524271976b625a4a605beefd893f270620fd751", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "nix-gitlab-ci": { - "locked": { - "dir": "lib", - "lastModified": 1752052838, - "narHash": "sha256-EqP4xB8YTVXWPCCchnVtQbuq0bKa79TUEcPF3hjuX/k=", - "owner": "TECHNOFAB", - "repo": "nix-gitlab-ci", - "rev": "0c6949f585a2c1ea2cf85fc01445496f7c75faae", - "type": "gitlab" - }, - "original": { - "dir": "lib", - "owner": "TECHNOFAB", - "repo": "nix-gitlab-ci", - "type": "gitlab" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1756696532, - "narHash": "sha256-6FWagzm0b7I/IGigOv9pr6LL7NQ86mextfE8g8Q6HBg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "58dcbf1ec551914c3756c267b8b9c8c86baa1b2f", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1754788789, - "narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "a73b9c743612e4244d865a2fdee11865283c04e6", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-parts": "flake-parts", - "nix-gitlab-ci": "nix-gitlab-ci", - "nixpkgs": "nixpkgs", - "systems": "systems" - } - }, - "systems": { - "locked": { - "lastModified": 1689347949, - "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", - "owner": "nix-systems", - "repo": "default-linux", - "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default-linux", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/examples/flake-parts/flake.nix b/examples/flake-parts/flake.nix deleted file mode 100644 index c1b7977..0000000 --- a/examples/flake-parts/flake.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ - outputs = { - flake-parts, - systems, - ... - } @ inputs: - flake-parts.lib.mkFlake {inherit inputs;} { - imports = [ - inputs.nix-gitlab-ci.flakeModule - ]; - systems = import systems; - flake = {}; - perSystem = _: { - ci = { - pipelines = { - "default" = { - stages = ["example"]; - jobs."example" = { - stage = "example"; - script = ["echo hello world"]; - }; - }; - "test".jobs."example" = { - stage = ".pre"; - script = ["echo hello world"]; - }; - }; - }; - }; - }; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; - systems.url = "github:nix-systems/default-linux"; - # NOTE: better pin to a version - nix-gitlab-ci.url = "gitlab:TECHNOFAB/nix-gitlab-ci?dir=lib"; - }; -} diff --git a/examples/rensa-nix/flake.lock b/examples/rensa-nix/flake.lock deleted file mode 100644 index 7c47f5c..0000000 --- a/examples/rensa-nix/flake.lock +++ /dev/null @@ -1,63 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1756542300, - "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1754184128, - "narHash": "sha256-AjhoyBL4eSyXf01Bmc6DiuaMrJRNdWopmdnMY0Pa/M0=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "02e72200e6d56494f4a7c0da8118760736e41b60", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "ren": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "dir": "lib", - "lastModified": 1756370106, - "narHash": "sha256-l84ojcHuQWBwn4BRxQsMMfQpcq/Az/sHh/hSqFgVtyg=", - "owner": "rensa-nix", - "repo": "core", - "rev": "9c1a29fa9ba7cbbb78b9e47eb8afbcd29303a3b4", - "type": "gitlab" - }, - "original": { - "dir": "lib", - "owner": "rensa-nix", - "repo": "core", - "type": "gitlab" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs", - "ren": "ren" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/examples/rensa-nix/flake.nix b/examples/rensa-nix/flake.nix deleted file mode 100644 index 1f81895..0000000 --- a/examples/rensa-nix/flake.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - ren.url = "gitlab:rensa-nix/core?dir=lib"; - }; - - outputs = { - self, - ren, - ... - } @ inputs: - ren.buildWith - { - inherit inputs; - cellsFrom = ./nix; - transformInputs = system: i: - i - // { - pkgs = import i.nixpkgs {inherit system;}; - }; - cellBlocks = with ren.blocks; [ - (simple "ci") - ]; - } - { - packages = ren.select self [ - ["repo" "ci" "packages"] - ]; - }; -} diff --git a/examples/rensa-nix/nix/repo/ci.nix b/examples/rensa-nix/nix/repo/ci.nix deleted file mode 100644 index adb707e..0000000 --- a/examples/rensa-nix/nix/repo/ci.nix +++ /dev/null @@ -1,18 +0,0 @@ -{inputs, ...}: let - inherit (inputs) cilib; -in - cilib.mkCI { - pipelines = { - "default" = { - stages = ["example"]; - jobs."example" = { - stage = "example"; - script = ["echo hello world"]; - }; - }; - "test".jobs."example" = { - stage = ".pre"; - script = ["echo hello world"]; - }; - }; - } diff --git a/examples/rensa-nix/nix/repo/flake.lock b/examples/rensa-nix/nix/repo/flake.lock deleted file mode 100644 index fb28eb7..0000000 --- a/examples/rensa-nix/nix/repo/flake.lock +++ /dev/null @@ -1,28 +0,0 @@ -{ - "nodes": { - "nix-gitlab-ci-lib": { - "locked": { - "dir": "lib", - "lastModified": 1752052838, - "narHash": "sha256-EqP4xB8YTVXWPCCchnVtQbuq0bKa79TUEcPF3hjuX/k=", - "owner": "TECHNOFAB", - "repo": "nix-gitlab-ci", - "rev": "0c6949f585a2c1ea2cf85fc01445496f7c75faae", - "type": "gitlab" - }, - "original": { - "dir": "lib", - "owner": "TECHNOFAB", - "repo": "nix-gitlab-ci", - "type": "gitlab" - } - }, - "root": { - "inputs": { - "nix-gitlab-ci-lib": "nix-gitlab-ci-lib" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/examples/rensa-nix/nix/repo/flake.nix b/examples/rensa-nix/nix/repo/flake.nix deleted file mode 100644 index ad3b77d..0000000 --- a/examples/rensa-nix/nix/repo/flake.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ - inputs = { - nix-gitlab-ci-lib.url = "gitlab:TECHNOFAB/nix-gitlab-ci?dir=lib"; - }; - outputs = i: - i - // { - cilib = i.nix-gitlab-ci-lib.lib {inherit (i.parent) pkgs;}; - }; -} diff --git a/flake.lock b/flake.lock index 7c47f5c..38bc581 100644 --- a/flake.lock +++ b/flake.lock @@ -1,12 +1,246 @@ { "nodes": { + "cachix": { + "inputs": { + "devenv": [ + "devenv" + ], + "flake-compat": [ + "devenv" + ], + "git-hooks": [ + "devenv" + ], + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1737621947, + "narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=", + "owner": "cachix", + "repo": "cachix", + "rev": "f65a3cd5e339c223471e64c051434616e18cc4f5", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "latest", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat", + "git-hooks": "git-hooks", + "nix": "nix", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1743687534, + "narHash": "sha256-QIh5jKWE0aZ4N77Zu3kz0RjA22zqqy5p6PfJnOW5rPE=", + "owner": "cachix", + "repo": "devenv", + "rev": "efd5d68a6483573410565d0b940e1a67b6f92591", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": [ + "devenv" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1740849354, + "narHash": "sha256-oy33+t09FraucSZ2rZ6qnD1Y1c8azKKmQuCvF2ytUko=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "4a709a8ce9f8c08fa7ddb86761fe488ff7858a07", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "libgit2": { + "flake": false, + "locked": { + "lastModified": 1697646580, + "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "type": "github" + }, + "original": { + "owner": "libgit2", + "repo": "libgit2", + "type": "github" + } + }, + "mkdocs-material-umami": { + "locked": { + "lastModified": 1745840856, + "narHash": "sha256-1Ad1JTMQMP6YsoIKAA+SBCE15qWrYkGue9/lXOLnu9I=", + "owner": "technofab", + "repo": "mkdocs-material-umami", + "rev": "3ac9b194450f6b779c37b8d16fec640198e5cd0a", + "type": "gitlab" + }, + "original": { + "owner": "technofab", + "repo": "mkdocs-material-umami", + "type": "gitlab" + } + }, + "nix": { + "inputs": { + "flake-compat": [ + "devenv" + ], + "flake-parts": "flake-parts", + "libgit2": "libgit2", + "nixpkgs": "nixpkgs_2", + "nixpkgs-23-11": [ + "devenv" + ], + "nixpkgs-regression": [ + "devenv" + ], + "pre-commit-hooks": [ + "devenv" + ] + }, + "locked": { + "lastModified": 1741798497, + "narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=", + "owner": "domenkozar", + "repo": "nix", + "rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.24", + "repo": "nix", + "type": "github" + } + }, + "nix-mkdocs": { + "locked": { + "dir": "lib", + "lastModified": 1745175318, + "narHash": "sha256-eJOTw3SK4psqBPP2hNJ4D/13/oi/FLoM0tYKoCGVFP8=", + "owner": "technofab", + "repo": "nixmkdocs", + "rev": "6d6e0139060c896ae14de4b9c82335655a384643", + "type": "gitlab" + }, + "original": { + "dir": "lib", + "owner": "technofab", + "repo": "nixmkdocs", + "type": "gitlab" + } + }, "nixpkgs": { "locked": { - "lastModified": 1756542300, - "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=", + "lastModified": 1733212471, + "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa", + "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", "type": "github" }, "original": { @@ -18,11 +252,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1754184128, - "narHash": "sha256-AjhoyBL4eSyXf01Bmc6DiuaMrJRNdWopmdnMY0Pa/M0=", + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "02e72200e6d56494f4a7c0da8118760736e41b60", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", "type": "github" }, "original": { @@ -31,30 +265,131 @@ "type": "github" } }, - "ren": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" + "nixpkgs_2": { + "locked": { + "lastModified": 1717432640, + "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", + "type": "github" }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1733477122, + "narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1745377448, + "narHash": "sha256-jhZDfXVKdD7TSEGgzFJQvEEZ2K65UMiqW5YJ2aIqxMA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "507b63021ada5fee621b6ca371c4fca9ca46f52c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_5": { + "locked": { + "lastModified": 1735554305, + "narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "0e82ab234249d8eee3e8c91437802b32c74bb3fd", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixtest": { "locked": { "dir": "lib", - "lastModified": 1756370106, - "narHash": "sha256-l84ojcHuQWBwn4BRxQsMMfQpcq/Az/sHh/hSqFgVtyg=", - "owner": "rensa-nix", - "repo": "core", - "rev": "9c1a29fa9ba7cbbb78b9e47eb8afbcd29303a3b4", + "lastModified": 1748945995, + "narHash": "sha256-OWflapMq78nCUT7N0vS/AuK4E8y7p8oh0zjnDioJ4Lw=", + "owner": "technofab", + "repo": "nixtest", + "rev": "392e8796f386373a36aecd3216925accf2714a1a", "type": "gitlab" }, "original": { "dir": "lib", - "owner": "rensa-nix", - "repo": "core", + "owner": "technofab", + "ref": "1.0.0", + "repo": "nixtest", "type": "gitlab" } }, "root": { "inputs": { - "nixpkgs": "nixpkgs", - "ren": "ren" + "devenv": "devenv", + "flake-parts": "flake-parts_2", + "mkdocs-material-umami": "mkdocs-material-umami", + "nix-mkdocs": "nix-mkdocs", + "nixpkgs": "nixpkgs_4", + "nixtest": "nixtest", + "systems": "systems", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": "nixpkgs_5" + }, + "locked": { + "lastModified": 1743677901, + "narHash": "sha256-eWZln+k+L/VHO69tUTzEmgeDWNQNKIpSUa9nqQgBrSE=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "57dabe2a6255bd6165b2437ff6c2d1f6ee78421a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index 769a6b9..0788dc0 100644 --- a/flake.nix +++ b/flake.nix @@ -1,37 +1,304 @@ { - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - ren.url = "gitlab:rensa-nix/core?dir=lib"; - }; - outputs = { - self, - ren, + flake-parts, + systems, ... } @ inputs: - ren.buildWith - { - inherit inputs; - cellsFrom = ./nix; - transformInputs = system: i: - i - // { - pkgs = import i.nixpkgs {inherit system;}; + flake-parts.lib.mkFlake {inherit inputs;} { + imports = [ + inputs.devenv.flakeModule + inputs.treefmt-nix.flakeModule + inputs.nix-mkdocs.flakeModule + inputs.nixtest.flakeModule + ./lib/flakeModule.nix + ]; + systems = import systems; + flake = {}; + perSystem = { + pkgs, + config, + system, + ... + }: rec { + imports = [ + ./tests + ]; + treefmt = { + projectRootFile = "flake.nix"; + programs = { + alejandra.enable = true; + mdformat.enable = true; + yamlfmt.enable = true; + }; + settings.formatter = { + yamlfmt.excludes = ["templates/nix-gitlab-ci.yml"]; + mdformat.command = let + pkg = pkgs.python3.withPackages (p: [ + p.mdformat + p.mdformat-mkdocs + ]); + in "${pkg}/bin/mdformat"; + }; }; - cellBlocks = with ren.blocks; [ - (simple "devShells") - (simple "pkgs") - (simple "tests") - (simple "docs") - (simple "ci") - ]; - } - { - packages = ren.select self [ - ["repo" "tests"] - ["repo" "docs"] - ["repo" "ci" "packages"] - ["packages" "pkgs"] - ]; + devenv.shells.default = { + containers = pkgs.lib.mkForce {}; + packages = with pkgs; [dive skopeo]; + + pre-commit = { + hooks = { + treefmt = { + enable = true; + packageOverrides.treefmt = config.treefmt.build.wrapper; + }; + }; + }; + }; + doc = { + path = ./docs; + deps = pp: [ + pp.mkdocs-material + (pp.callPackage inputs.mkdocs-material-umami {}) + ]; + config = { + site_name = "Nix GitLab CI"; + repo_name = "TECHNOFAB/nix-gitlab-ci"; + repo_url = "https://gitlab.com/TECHNOFAB/nix-gitlab-ci"; + edit_uri = "edit/main/docs/"; + theme = { + name = "material"; + features = ["content.code.copy" "content.action.edit"]; + icon.repo = "simple/gitlab"; + logo = "images/logo.png"; + favicon = "images/favicon.png"; + palette = [ + { + scheme = "default"; + media = "(prefers-color-scheme: light)"; + primary = "deep orange"; + accent = "orange"; + toggle = { + icon = "material/brightness-7"; + name = "Switch to dark mode"; + }; + } + { + scheme = "slate"; + media = "(prefers-color-scheme: dark)"; + primary = "deep orange"; + accent = "orange"; + toggle = { + icon = "material/brightness-4"; + name = "Switch to light mode"; + }; + } + ]; + }; + plugins = ["search" "material-umami"]; + nav = [ + {"Introduction" = "index.md";} + {"Setup" = "setup.md";} + {"Usage" = "usage.md";} + {"CI/CD Component" = "cicd_component.md";} + {"Environment Variables" = "environment_variables.md";} + {"Caching" = "caching.md";} + {"Multiple Pipelines" = "multi_pipeline.md";} + {"Utilities" = "utilities.md";} + {"Kubernetes Runner Example" = "kubernetes_runner.md";} + {"Example Configs" = "examples.md";} + ]; + markdown_extensions = [ + { + "pymdownx.highlight".pygments_lang_class = true; + } + "pymdownx.inlinehilite" + "pymdownx.snippets" + "pymdownx.superfences" + "fenced_code" + "admonition" + ]; + extra.analytics = { + provider = "umami"; + site_id = "28f7c904-db22-4c2b-9ee4-ed42e14b6db9"; + src = "https://analytics.tf/umami"; + domains = "nix-gitlab-ci.projects.tf"; + feedback = { + title = "Was this page helpful?"; + ratings = [ + { + icon = "material/thumb-up-outline"; + name = "This page is helpful"; + data = "good"; + note = "Thanks for your feedback!"; + } + { + icon = "material/thumb-down-outline"; + name = "This page could be improved"; + data = "bad"; + note = "Thanks for your feedback! Please leave feedback by creating an issue :)"; + } + ]; + }; + }; + }; + }; + # should set the "default" pipeline + ci = { + stages = ["test" "nixtest" "build" "deploy"]; + jobs = { + "test" = { + stage = "test"; + nix = { + deps = [pkgs.hello pkgs.curl]; + enable-runner-cache = true; + }; + variables = { + TEST = "test"; + TEST_WITH_DERIVATION = "${pkgs.hello}/test"; + }; + script = [ + "hello" + "curl google.de" + "echo $TEST $TEST_WITH_DERIVATION" + ]; + }; + "test-default" = { + stage = "test"; + nix.deps = [pkgs.hello]; + script = ["hello"]; + }; + "test-non-nix" = { + nix.enable = false; + stage = "test"; + image = "alpine:latest"; + script = [ + "echo \"This job will not be modified to use nix\"" + ]; + }; + # -- actually useful jobs -- + "docs" = { + stage = "build"; + script = [ + # sh + '' + nix build .#docs:default + mkdir -p public + cp -r result/. public/ + '' + ]; + artifacts.paths = ["public"]; + }; + "pages" = { + nix.enable = false; + image = "alpine:latest"; + stage = "deploy"; + script = ["true"]; + artifacts.paths = ["public"]; + rules = [ + { + "if" = "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"; + } + ]; + }; + "nixtest" = { + stage = "nixtest"; + script = [ + # sh + "nix run .#nixtests:run -- --junit=junit.xml --pure" + ]; + allow_failure = true; + artifacts = { + when = "always"; + reports.junit = "junit.xml"; + }; + }; + }; + }; + pipelines."non-default" = { + stages = ["test"]; + jobs = { + "test" = { + stage = "test"; + script = [ + "echo Hello from another pipeline" + ]; + }; + }; + }; + + packages = let + setupScript = pkgs.writeShellScriptBin "setup_nix_ci" (builtins.readFile ./scripts/setup_nix_ci.sh); + finalizeScript = pkgs.writeShellScriptBin "finalize_nix_ci" (builtins.readFile ./scripts/finalize_nix_ci.sh); + in { + setup-script = setupScript; + finalize-script = finalizeScript; + image = pkgs.dockerTools.buildImage { + name = "nix-ci"; + fromImage = let + hashes = { + "x86_64-linux" = "sha256-kJ7dqje5o1KPr3RDZ7/THbhMSoiCU1C/7HshDrNfwnM="; + "aarch64-linux" = "sha256-jz+Z3Ji+hy5d9ImOh/YOKCqy9P9/cseSov+5J/O95bg="; + }; + # check digest of tags like nixos-24.11-aarch64-linux etc. + digests = { + "x86_64-linux" = "sha256:345f210dea4cbd049e2d01d13159c829066dfb6e273cdd49ea878186d17b19f7"; + "aarch64-linux" = "sha256:66163fdf446d851416dd4e9be28c0794d9c2550214a57a846957699a3f5747f6"; + }; + hash = hashes.${system} or (throw "Unsupported system"); + imageDigest = digests.${system} or (throw "Unsupported system"); + in + pkgs.dockerTools.pullImage { + imageName = "nixpkgs/nix-flakes"; + inherit hash imageDigest; + }; + copyToRoot = pkgs.buildEnv { + name = "image-root"; + paths = with pkgs; + [ + gitMinimal + gnugrep + gnused + coreutils + diffutils + cachix + attic-client + ] + ++ [ + setupScript + finalizeScript + ]; + pathsToLink = ["/bin"]; + }; + }; + }; + + checks = packages; + }; }; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + # flake & devenv related + flake-parts.url = "github:hercules-ci/flake-parts"; + systems.url = "github:nix-systems/default-linux"; + devenv.url = "github:cachix/devenv"; + treefmt-nix.url = "github:numtide/treefmt-nix"; + nix-mkdocs.url = "gitlab:technofab/nixmkdocs?dir=lib"; + mkdocs-material-umami.url = "gitlab:technofab/mkdocs-material-umami"; + nixtest.url = "gitlab:technofab/nixtest/1.0.0?dir=lib"; + }; + + nixConfig = { + extra-substituters = [ + "https://cache.nixos.org/" + "https://nix-community.cachix.org" + "https://devenv.cachix.org" + ]; + + extra-trusted-public-keys = [ + "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=" + ]; + }; } diff --git a/lib/VERSION b/lib/VERSION deleted file mode 100644 index 4a36342..0000000 --- a/lib/VERSION +++ /dev/null @@ -1 +0,0 @@ -3.0.0 diff --git a/lib/default.nix b/lib/default.nix index ff3ec88..37f6318 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,29 +1,8 @@ -args: let - # allow passing just pkgs aswell for convenience - lib = args.lib or args.pkgs.lib; - # makes it optional to pass if it's not explicitly needed - pkgs = args.pkgs or (throw "[nix-gitlab-ci] pkgs argument was used but not set, please pass it"); - inherit (lib) evalModules trimWith; - - impl = import ./impl {inherit lib pkgs cilib;}; - - cilib = { - inherit (impl) helpers modules mkPipeline mkJobRun mkJobDeps mkJobPatched; - utils = import ./utils.nix {inherit pkgs;}; - version = trimWith { - start = true; - end = true; - } (builtins.readFile ./VERSION); - - mkCI = config: - (evalModules { - modules = [ - cilib.modules.nixCiSubmodule - { - inherit config; - } - ]; - }).config; - }; -in - cilib +args: { + helpers = import ./helpers.nix args; + jobDeps = import ./jobDeps.nix args; + jobRun = import ./jobRun.nix args; + jobPatch = import ./jobPatch.nix args; + pipeline = import ./pipeline.nix args; + utils = import ./utils.nix args; +} diff --git a/lib/flake.nix b/lib/flake.nix index 227f9a6..b2cc77a 100644 --- a/lib/flake.nix +++ b/lib/flake.nix @@ -1,6 +1,8 @@ { - outputs = _i: { - lib = import ./.; - flakeModule = ./flakeModule.nix; + description = "Nix-GitLab-CI lib"; + + outputs = {...}: { + flakeModule = import ./flakeModule.nix; + lib = import ./default.nix; }; } diff --git a/lib/flakeModule.nix b/lib/flakeModule.nix index aadf292..4f8a274 100644 --- a/lib/flakeModule.nix +++ b/lib/flakeModule.nix @@ -10,15 +10,163 @@ ... }: let cilib = import ./. {inherit lib pkgs;}; + inherit (cilib.pipeline) mkPipeline; inherit (lib) types mkOption; - in { - options = { - ci = mkOption { - type = types.submodule cilib.modules.nixCiSubmodule; + + cfg = config.ci.config; + + subType = options: types.submodule {inherit options;}; + mkNullOption = type: + mkOption { + default = null; + type = types.nullOr type; + }; + + configType = subType { + nix-jobs-by-default = mkOption { + type = types.bool; + default = true; + description = "Handle jobs nix-based by default or via opt-in (in a job set nix.enable = true) if false"; + }; + }; + jobType = subType { + # nix ci opts + nix = mkOption { + type = subType { + enable = mkOption { + type = types.bool; + default = cfg.nix-jobs-by-default; + description = "Handle this job as a nix job"; + }; + deps = mkOption { + type = types.listOf types.package; + default = []; + description = "Dependencies/packages to install for this job"; + }; + enable-runner-cache = 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. + ''; + }; + runner-cache-key = mkOption { + type = types.str; + default = "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"; + description = "Cache key to use for the runner nix cache. Requires enable-runner-cache = true"; + }; + }; + default = {}; + description = "Configure Nix Gitlab CI for each job individually"; + }; + # gitlab opts + script = mkOption { + type = types.listOf types.str; + default = []; + }; + stage = mkOption { + type = types.str; + default = "test"; + }; + image = mkOption { + type = types.str; + default = "$NIX_CI_IMAGE"; + }; + after_script = mkNullOption (types.listOf types.str); + allow_failure = mkNullOption (types.either types.attrs types.bool); + artifacts = mkNullOption (types.attrs); + before_script = mkNullOption (types.listOf types.str); + cache = mkNullOption (types.either (types.listOf types.attrs) types.attrs); + coverage = mkNullOption (types.str); + dependencies = mkNullOption (types.listOf types.str); + environment = mkNullOption (types.either types.attrs types.str); + extends = mkNullOption (types.str); + hooks = mkNullOption (types.attrs); + id_tokens = mkNullOption (types.attrs); + "inherit" = mkNullOption (types.attrs); + interruptible = mkNullOption (types.bool); + needs = mkNullOption (types.listOf (types.either types.str types.attrs)); + publish = mkNullOption (types.str); + pages = mkNullOption (types.attrs); + parallel = mkNullOption (types.either types.int types.attrs); + release = mkNullOption (types.attrs); + retry = mkNullOption (types.either types.int types.attrs); + rules = mkNullOption (types.listOf types.attrs); + resource_group = mkNullOption (types.str); + secrets = mkNullOption (types.attrs); + services = mkNullOption (types.listOf types.attrs); + start_in = mkNullOption (types.str); + tags = mkNullOption (types.listOf types.str); + timeout = mkNullOption (types.str); + variables = mkNullOption (types.attrs); + when = mkNullOption (types.str); + }; + + ciType = subType { + config = mkOption { + type = configType; + description = '' + Configuration options for the nix part itself + ''; + default = {}; + }; + image = mkNullOption (types.str); + variables = mkNullOption (types.attrs); + default = mkNullOption (types.attrs); + stages = mkNullOption (types.listOf types.str); + include = mkNullOption (types.attrs); + workflow = mkNullOption (types.attrs); + jobs = mkOption { + type = types.lazyAttrsOf jobType; default = {}; }; }; - config.legacyPackages = config.ci.packages; + in { + options = { + pipelines = mkOption { + type = types.lazyAttrsOf ciType; + description = '' + Create multiple GitLab CI pipelines. + + See README.md for more information about how a pipeline is selected. + ''; + default = {}; + apply = op: let + # NOTE: show warning if "default" is set and config.ci is not {} + legacyMode = config.ci != {}; + defaultExists = builtins.hasAttr "default" op; + value = + { + "default" = config.ci; + } + // op; + in + if defaultExists && legacyMode + then builtins.trace "Warning: config.ci is overwritten by pipelines.default" value + else value; + }; + ci = mkOption { + type = ciType; + description = '' + Note: this is a shorthand for writing `pipelines."default"` + + 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.legacyPackages = lib.fold (pipeline: acc: acc // pipeline) {} ( + map ( + pipeline_name: + (mkPipeline { + pipeline = config.pipelines."${pipeline_name}"; + name = pipeline_name; + }).packages + ) (builtins.attrNames config.pipelines) + ); } ); } diff --git a/lib/helpers.nix b/lib/helpers.nix new file mode 100644 index 0000000..3b4c057 --- /dev/null +++ b/lib/helpers.nix @@ -0,0 +1,49 @@ +{lib, ...} @ args: let + inherit (lib) isAttrs filterAttrs mapAttrs; +in rec { + prepend = key: arr: job: + job + // lib.optionalAttrs (job.nix.enable or false) { + ${key} = + arr + ++ (job.${key} or []); + }; + append = key: arr: job: + job + // lib.optionalAttrs (job.nix.enable or false) { + ${key} = (job.${key} or []) ++ arr; + }; + prependToBeforeScript = prepend "before_script"; + appendToAfterScript = append "after_script"; + + # json is also valid yaml and this removes dependency on jq and/or remarshal + # (used in pkgs.formats.json and pkgs.formats.yaml respectively) + toYaml = name: value: builtins.toFile name (builtins.toJSON value); + + customMapAttrs = cb: set: builtins.listToAttrs (builtins.map (key: cb key (builtins.getAttr key set)) (builtins.attrNames set)); + + filterAttrsRec = pred: v: + if isAttrs v + then filterAttrs pred (mapAttrs (path: filterAttrsRec pred) v) + else v; + + # filter job's variables to either only those containing store paths + # or those that do not + filterJobVariables = nix: job: + lib.concatMapAttrs ( + name: value: + lib.optionalAttrs ((lib.hasInfix "/nix/store/" value) == nix) { + ${name} = value; + } + ) + (job.variables or {}); + + # args.pkgs so "pkgs" does not need to be passed all the time + stdenvMinimal = args.pkgs.stdenvNoCC.override { + cc = null; + preHook = ""; + allowedRequisites = null; + initialPath = with args.pkgs; [coreutils findutils]; + extraNativeBuildInputs = []; + }; +} diff --git a/lib/impl/default.nix b/lib/impl/default.nix deleted file mode 100644 index 7ff5623..0000000 --- a/lib/impl/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ - pkgs, - lib, - cilib, -}: rec { - helpers = import ./helpers.nix {inherit lib pkgs;}; - mkJobDeps = import ./jobDeps.nix {inherit lib helpers;}; - mkJobRun = import ./jobRun.nix {inherit lib pkgs helpers;}; - mkJobPatched = import ./jobPatched.nix {inherit lib helpers;}; - mkPipeline = import ./pipeline.nix {inherit lib helpers mkJobDeps mkJobRun mkJobPatched;}; - modules = import ./modules {inherit lib cilib;}; -} diff --git a/lib/impl/helpers.nix b/lib/impl/helpers.nix deleted file mode 100644 index 48a79fc..0000000 --- a/lib/impl/helpers.nix +++ /dev/null @@ -1,123 +0,0 @@ -{ - pkgs, - lib, -} @ args: let - inherit - (lib) - types - isAttrs - filterAttrs - mapAttrs - mkOption - mkOptionType - isType - literalExpression - pipe - hasInfix - concatMapAttrs - optionalAttrs - ; -in rec { - prepend = key: arr: job: { - ${key} = arr ++ (job.${key} or []); - }; - append = key: arr: job: { - ${key} = (job.${key} or []) ++ arr; - }; - prependToBeforeScript = prepend "before_script"; - appendToAfterScript = append "after_script"; - - # json is also valid yaml and this removes dependency on jq and/or remarshal - # (used in pkgs.formats.json and pkgs.formats.yaml respectively) - toYaml = name: value: - pipe value [ - builtins.toJSON - builtins.unsafeDiscardOutputDependency - builtins.unsafeDiscardStringContext - (builtins.toFile name) - ]; - toYamlPretty = (pkgs.formats.yaml {}).generate; - - customMapAttrs = cb: set: builtins.listToAttrs (builtins.map (key: cb key (builtins.getAttr key set)) (builtins.attrNames set)); - - filterAttrsRec = pred: v: - if isAttrs v - then filterAttrs pred (mapAttrs (_path: filterAttrsRec pred) v) - else v; - - # filter job's variables to either only those containing store paths - # or those that do not - filterJobVariables = shouldContain: job: - concatMapAttrs ( - name: value: - optionalAttrs ((hasInfix "/nix/store/" value) == shouldContain) { - ${name} = value; - } - ) - (job.variables or {}); - - deepMerge = lhs: rhs: - lhs - // rhs - // (builtins.mapAttrs ( - rName: rValue: let - lValue = lhs.${rName} or null; - in - if builtins.isAttrs lValue && builtins.isAttrs rValue - then deepMerge lValue rValue - else if builtins.isList lValue && builtins.isList rValue - then lValue ++ rValue - else rValue - ) - rhs); - - unsetType = mkOptionType { - name = "unset"; - description = "unset"; - descriptionClass = "noun"; - check = _value: true; - }; - unset = { - _type = "unset"; - }; - isUnset = isType "unset"; - unsetOr = typ: - (types.either unsetType typ) - // { - inherit (typ) description getSubOptions; - }; - mkUnsetOption = opts: - mkOption (opts - // { - type = unsetOr opts.type; - default = opts.default or 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: - if builtins.isAttrs value && !builtins.hasAttr "_type" value - then let - filteredAttrs = builtins.mapAttrs (_n: filterUnset) value; - in - filterAttrs (_name: value: (!isUnset value)) filteredAttrs - else if builtins.isList value - then builtins.filter (elem: !isUnset elem) (map filterUnset value) - else value; - - # args.pkgs so "pkgs" does not need to be passed all the time - stdenvMinimal = args.pkgs.stdenvNoCC.override { - cc = null; - preHook = ""; - allowedRequisites = null; - initialPath = with args.pkgs; [coreutils findutils]; - extraNativeBuildInputs = []; - }; -} diff --git a/lib/impl/jobPatched.nix b/lib/impl/jobPatched.nix deleted file mode 100644 index c7134fb..0000000 --- a/lib/impl/jobPatched.nix +++ /dev/null @@ -1,43 +0,0 @@ -{ - lib, - helpers, -}: let - inherit (lib) toList optionalAttrs optional; - inherit (helpers) prependToBeforeScript appendToAfterScript filterJobVariables; -in - { - key, - job, - pipelineName, - nixConfig, - }: - (builtins.removeAttrs job ["variables" "cache"]) - // (optionalAttrs nixConfig.enable ( - (prependToBeforeScript ["source setup_nix_ci \"gitlab-ci:pipeline:${pipelineName}:job-deps:${key}\""] job) - // (appendToAfterScript ["finalize_nix_ci"] job) - )) - // optionalAttrs nixConfig.enable ( - (let - variables = - (filterJobVariables false job) - // optionalAttrs nixConfig.enableRunnerCache { - NIX_CI_CACHE_STRATEGY = "runner"; - }; - in - # filter empty variables - optionalAttrs (variables != {}) { - inherit variables; - }) - // (let - cache = - (toList (job.cache or [])) - ++ (optional nixConfig.enableRunnerCache { - key = nixConfig.runnerCacheKey; - paths = [".nix-cache/"]; - }); - in - # filter empty cache - optionalAttrs (cache != []) { - inherit cache; - }) - ) diff --git a/lib/impl/modules/default.nix b/lib/impl/modules/default.nix deleted file mode 100644 index 9e33a7e..0000000 --- a/lib/impl/modules/default.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ - lib, - cilib, -}: rec { - inherit - (import ./root.nix { - inherit lib pipelineSubmodule soonixSubmodule; - }) - configSubmodule - nixCiSubmodule - ; - inherit - (import ./pipeline.nix { - inherit lib cilib jobSubmodule; - }) - pipelineConfigSubmodule - pipelineSubmodule - ; - inherit - (import ./job.nix { - inherit lib cilib; - }) - jobConfigSubmodule - jobSubmodule - ; - inherit - (import ./soonix.nix { - inherit lib cilib; - }) - soonixSubmodule - ; -} diff --git a/lib/impl/modules/job.nix b/lib/impl/modules/job.nix deleted file mode 100644 index 5f7d1ee..0000000 --- a/lib/impl/modules/job.nix +++ /dev/null @@ -1,663 +0,0 @@ -{ - 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; - in { - finalConfig = cilib.mkJobPatched { - key = name; - job = filterUnset (filterAttrs (n: _v: builtins.elem n attrsToKeep) config); - nixConfig = config.nix; - inherit pipelineName; - }; - depsDrv = cilib.mkJobDeps { - key = name; - job = config.finalConfig; - nixConfig = config.nix; - }; - runnerDrv = cilib.mkJobRun { - key = name; - job = config.finalConfig; - jobDeps = config.depsDrv; - }; - packages = { - "gitlab-ci:pipeline:${pipelineName}:job-deps:${name}" = config.depsDrv; - "gitlab-ci:pipeline:${pipelineName}:job:${name}" = config.runnerDrv; - }; - }; - }; -} diff --git a/lib/impl/modules/pipeline.nix b/lib/impl/modules/pipeline.nix deleted file mode 100644 index 32b9e0c..0000000 --- a/lib/impl/modules/pipeline.nix +++ /dev/null @@ -1,228 +0,0 @@ -{ - 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, - ... - }: 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) variable’s 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; - in { - finalConfig = - (filterUnset (filterAttrs (n: _v: builtins.elem n attrsToKeep) config)) - // mapAttrs (_name: value: value.finalConfig) config.jobs; - 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; -} diff --git a/lib/impl/modules/root.nix b/lib/impl/modules/root.nix deleted file mode 100644 index 84fb5ba..0000000 --- a/lib/impl/modules/root.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ - lib, - soonixSubmodule, - pipelineSubmodule, - ... -}: let - inherit (lib) mkOption types; -in rec { - configSubmodule = { - options = { - soonix = mkOption { - description = '' - Configure the soonix `.gitlab-ci.yml` generation. - ''; - type = types.submodule soonixSubmodule; - default = {}; - }; - nixJobsByDefault = mkOption { - description = '' - Whether to transform all jobs to nix-configured jobs by default. - If false, you need to set [`nix.enable`](#pipelinesnamejobsnamenixenable) - for each job you want to be transformed. - ''; - type = types.bool; - default = true; - }; - }; - }; - - nixCiSubmodule = {config, ...}: { - options = { - config = mkOption { - description = '' - Configuration of Nix-GitLab-CI itself. - ''; - type = types.submodule configSubmodule; - default = {}; - }; - pipelines = mkOption { - description = '' - Defines all pipelines. - ''; - type = types.attrsOf (types.submoduleWith { - modules = [pipelineSubmodule]; - specialArgs.rootConfig = config.config; - }); - default = {}; - }; - - packages = mkOption { - description = '' - Final packages for use in CI. (readonly) - ''; - readOnly = true; - type = types.attrsOf types.package; - }; - soonix = mkOption { - description = '' - Soonix config for generating `.gitlab-ci.yml`. (readonly) - - See [`config.soonix`](#configsoonix) for configuring this. - ''; - readOnly = true; - type = types.attrs; - }; - }; - config = { - packages = lib.fold (pipeline: acc: acc // pipeline) {} ( - map (pipeline: pipeline.packages) (builtins.attrValues config.pipelines) - ); - soonix = config.config.soonix.finalConfig; - }; - }; -} diff --git a/lib/impl/modules/soonix.nix b/lib/impl/modules/soonix.nix deleted file mode 100644 index d23d727..0000000 --- a/lib/impl/modules/soonix.nix +++ /dev/null @@ -1,69 +0,0 @@ -{ - lib, - cilib, - ... -}: let - inherit (lib) mkOption types; -in { - soonixSubmodule = {config, ...}: { - options = { - componentVersion = mkOption { - description = '' - CI/CD component version. Also get's passed to inputs → version. - ''; - type = types.str; - default = cilib.version; - }; - componentUrl = mkOption { - description = '' - CI/CD component url. - ''; - type = types.str; - default = "gitlab.com/TECHNOFAB/nix-gitlab-ci/nix-gitlab-ci"; - }; - componentInputs = mkOption { - description = '' - Extra inputs to pass to the CI/CD component. - ''; - type = types.attrs; - default = {}; - }; - extraData = mkOption { - description = '' - Extra data to include in the `.gitlab-ci.yml` file. - ''; - type = types.attrs; - default = {}; - }; - - finalConfig = mkOption { - internal = true; - type = types.attrs; - }; - }; - config.finalConfig = { - opts.format = "yaml"; - hook = { - mode = "copy"; - gitignore = false; - }; - output = ".gitlab-ci.yml"; - generator = "nix"; - data = - cilib.helpers.deepMerge - { - include = [ - { - component = "${config.componentUrl}@${config.componentVersion}"; - inputs = - { - version = config.componentVersion; - } - // config.componentInputs; - } - ]; - } - config.extraData; - }; - }; -} diff --git a/lib/impl/jobDeps.nix b/lib/jobDeps.nix similarity index 57% rename from lib/impl/jobDeps.nix rename to lib/jobDeps.nix index 302597d..27bad19 100644 --- a/lib/impl/jobDeps.nix +++ b/lib/jobDeps.nix @@ -1,21 +1,21 @@ { lib, - helpers, + pkgs, + ... }: let - inherit (lib) concatLines mapAttrsToList makeBinPath; - inherit (helpers) filterJobVariables stdenvMinimal; -in - { + cilib = import ./. {inherit lib pkgs;}; + inherit (cilib.helpers) filterJobVariables stdenvMinimal; +in { + mkJobDeps = { key, job, - nixConfig, }: let variablesWithStorePaths = filterJobVariables true job; - variableExports = concatLines ( - mapAttrsToList (name: value: "export ${name}=\"${value}\"") variablesWithStorePaths + variableExports = lib.concatLines ( + lib.mapAttrsToList (name: value: "export ${name}=\"${value}\"") variablesWithStorePaths ); script = '' - export PATH="${makeBinPath (nixConfig.deps or [])}:$PATH"; + export PATH="${lib.makeBinPath (job.nix.deps or [])}:$PATH"; # variables containing nix derivations: ${variableExports} ''; @@ -32,4 +32,5 @@ in passthru = { inherit script; }; - } + }; +} diff --git a/lib/jobPatch.nix b/lib/jobPatch.nix new file mode 100644 index 0000000..959a9c0 --- /dev/null +++ b/lib/jobPatch.nix @@ -0,0 +1,49 @@ +{ + lib, + pkgs, + ... +}: let + cilib = import ./. {inherit lib pkgs;}; + inherit (lib) toList; + inherit (cilib.helpers) prependToBeforeScript appendToAfterScript filterJobVariables; +in { + mkJobPatched = { + key, + job, + pipeline_name, + }: + builtins.removeAttrs ( + (prependToBeforeScript [ + "source setup_nix_ci \"gitlab-ci:pipeline:${pipeline_name}:job-deps:${key}\"" + ] + (appendToAfterScript [ + "finalize_nix_ci" + ] + job)) + // lib.optionalAttrs job.nix.enable ( + (let + variables = + (filterJobVariables false job) + // lib.optionalAttrs job.nix.enable-runner-cache { + NIX_CI_CACHE_STRATEGY = "runner"; + }; + in + # filter empty variables + lib.optionalAttrs (variables != {}) { + inherit variables; + }) + // (let + cache = + (toList (job.cache or [])) + ++ (lib.optional (job.nix.enable-runner-cache) { + key = job.nix.runner-cache-key; + paths = [".nix-cache/"]; + }); + in + # filter empty cache + lib.optionalAttrs (cache != []) { + inherit cache; + }) + ) + ) ["nix"]; +} diff --git a/lib/impl/jobRun.nix b/lib/jobRun.nix similarity index 68% rename from lib/impl/jobRun.nix rename to lib/jobRun.nix index 4a1e0f6..5c4a118 100644 --- a/lib/impl/jobRun.nix +++ b/lib/jobRun.nix @@ -1,19 +1,19 @@ { lib, pkgs, - helpers, + ... }: let - inherit (lib) concatLines mapAttrsToList getExe; - inherit (helpers) filterJobVariables; -in - { + cilib = import ./. {inherit lib pkgs;}; + inherit (cilib.helpers) filterJobVariables; +in { + mkJobRun = { key, job, jobDeps, }: let variablesWithoutStorePaths = filterJobVariables false job; - variableExports = concatLines ( - mapAttrsToList (name: value: "export ${name}=\"${value}\"") variablesWithoutStorePaths + variableExports = lib.concatLines ( + lib.mapAttrsToList (name: value: "export ${name}=\"${value}\"") variablesWithoutStorePaths ); sandboxHelper = pkgs.writeShellScriptBin "gitlab-ci-job-sandbox-helper" (builtins.readFile ./sandbox_helper.sh); actualJobScript = pkgs.writeShellScript "gitlab-ci-job:${key}:raw" '' @@ -24,24 +24,25 @@ in # run before_script, script and after_script echo -e "\e[32mRunning before_script...\e[0m" set -x - ${concatLines (job.before_script or [])} + ${lib.concatLines (job.before_script or [])} { set +x; } 2>/dev/null echo -e "\e[32mRunning script...\e[0m" set -x - ${concatLines job.script} + ${lib.concatLines job.script} { set +x; } 2>/dev/null echo -e "\e[32mRunning after_script...\e[0m" set -x - ${concatLines (job.after_script or [])} + ${lib.concatLines (job.after_script or [])} { set +x; } 2>/dev/null ''; in # this way the sandbox helper just needs to be built once pkgs.writeShellScriptBin "gitlab-ci-job:${key}" '' - exec ${getExe sandboxHelper} ${actualJobScript} $@ + exec ${lib.getExe sandboxHelper} ${actualJobScript} $@ '' // { passthru = { inherit jobDeps actualJobScript; }; - } + }; +} diff --git a/lib/impl/pipeline.nix b/lib/pipeline.nix similarity index 67% rename from lib/impl/pipeline.nix rename to lib/pipeline.nix index 4d9b185..2873b22 100644 --- a/lib/impl/pipeline.nix +++ b/lib/pipeline.nix @@ -1,20 +1,20 @@ { lib, - helpers, - mkJobDeps, - mkJobRun, - mkJobPatched, + pkgs, + ... }: let - inherit (lib) assertMsg; - inherit (helpers) filterAttrsRec customMapAttrs toYaml toYamlPretty; -in - { + cilib = import ./. {inherit lib pkgs;}; + inherit (cilib.helpers) filterAttrsRec customMapAttrs toYaml; + inherit (cilib.jobDeps) mkJobDeps; + inherit (cilib.jobRun) mkJobRun; + inherit (cilib.jobPatch) mkJobPatched; +in { + mkPipeline = { name, pipeline, - nixConfig, }: let - jobs = filterAttrsRec (_n: v: v != null) pipeline.jobs; - rest = filterAttrsRec (_n: v: v != null) (builtins.removeAttrs pipeline ["jobs"]); + jobs = filterAttrsRec (n: v: v != null) pipeline.jobs; + rest = filterAttrsRec (n: v: v != null) (builtins.removeAttrs pipeline ["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 @@ -22,7 +22,7 @@ in jobsMappedForDeps = customMapAttrs (key: job: { name = "gitlab-ci:pipeline:${name}:job-deps:${key}"; - value = mkJobDeps {inherit key job nixConfig;}; + value = mkJobDeps {inherit key job;}; }) jobs; # allows the user to directly run the script @@ -30,7 +30,7 @@ in customMapAttrs (key: job: { name = "gitlab-ci:pipeline:${name}:job:${key}"; value = mkJobRun { - inherit key job nixConfig; + inherit key job; jobDeps = jobsMappedForDeps."gitlab-ci:pipeline:${name}:job-deps:${key}"; }; }) @@ -39,10 +39,10 @@ in jobsPatched = customMapAttrs (key: job: { name = key; - value = assert assertMsg (builtins.elem job.stage (rest.stages or [])) "stage '${job.stage}' of job '${key}' does not exist"; + value = assert lib.assertMsg (builtins.elem job.stage (rest.stages or [])) "stage '${job.stage}' of job '${key}' does not exist"; mkJobPatched { - inherit key job nixConfig; - pipelineName = name; + inherit key job; + pipeline_name = name; }; }) jobs; @@ -53,9 +53,9 @@ in # gitlab-ci:pipeline::job-deps: { "gitlab-ci:pipeline:${name}" = toYaml "gitlab-ci-${name}.yml" (rest // jobsPatched); - "gitlab-ci:pipeline:${name}:pretty" = toYamlPretty "gitlab-ci-${name}.yml" (rest // jobsPatched); } // jobsMappedForDeps // jobsMappedForScript; finalConfig = rest // jobsPatched; - } + }; +} diff --git a/lib/impl/sandbox_helper.sh b/lib/sandbox_helper.sh similarity index 100% rename from lib/impl/sandbox_helper.sh rename to lib/sandbox_helper.sh index 294fc6b..4ea5ec5 100644 --- a/lib/impl/sandbox_helper.sh +++ b/lib/sandbox_helper.sh @@ -35,7 +35,6 @@ done 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 else @@ -50,6 +49,7 @@ if [ "$NO_SANDBOX" = false ]; then git diff --staged > "$DIRTY_PATCH" trap "rm -f '$DIRTY_PATCH'" EXIT fi + TMPDIR=$(mktemp -dt "nix-gitlab-ci.XXX") git clone . $TMPDIR pushd $TMPDIR >/dev/null if [[ ! -z "$DIRTY_PATCH" && "$INCLUDE_DIRTY" = true ]]; then diff --git a/nix/packages/pkgs.nix b/nix/packages/pkgs.nix deleted file mode 100644 index 34d07aa..0000000 --- a/nix/packages/pkgs.nix +++ /dev/null @@ -1,48 +0,0 @@ -{ - inputs, - system, - ... -}: let - inherit (inputs) pkgs; -in rec { - setupScript = pkgs.writeShellScriptBin "setup_nix_ci" (builtins.readFile ./scripts/setup_nix_ci.sh); - finalizeScript = pkgs.writeShellScriptBin "finalize_nix_ci" (builtins.readFile ./scripts/finalize_nix_ci.sh); - image = pkgs.dockerTools.buildImage { - name = "nix-ci"; - fromImage = let - hashes = { - "x86_64-linux" = "sha256-kJ7dqje5o1KPr3RDZ7/THbhMSoiCU1C/7HshDrNfwnM="; - "aarch64-linux" = "sha256-jz+Z3Ji+hy5d9ImOh/YOKCqy9P9/cseSov+5J/O95bg="; - }; - # check digest of tags like nixos-24.11-aarch64-linux etc. - digests = { - "x86_64-linux" = "sha256:345f210dea4cbd049e2d01d13159c829066dfb6e273cdd49ea878186d17b19f7"; - "aarch64-linux" = "sha256:66163fdf446d851416dd4e9be28c0794d9c2550214a57a846957699a3f5747f6"; - }; - hash = hashes.${system} or (throw "Unsupported system"); - imageDigest = digests.${system} or (throw "Unsupported system"); - in - pkgs.dockerTools.pullImage { - imageName = "nixpkgs/nix-flakes"; - inherit hash imageDigest; - }; - copyToRoot = pkgs.buildEnv { - name = "image-root"; - paths = with pkgs; - [ - gitMinimal - gnugrep - gnused - coreutils - diffutils - cachix - attic-client - ] - ++ [ - setupScript - finalizeScript - ]; - pathsToLink = ["/bin"]; - }; - }; -} diff --git a/nix/repo/ci.nix b/nix/repo/ci.nix deleted file mode 100644 index a76c0d8..0000000 --- a/nix/repo/ci.nix +++ /dev/null @@ -1,99 +0,0 @@ -{inputs, ...}: let - inherit (inputs) cilib; -in - cilib.mkCI { - config.soonix = { - componentUrl = "$CI_SERVER_FQDN/$CI_PROJECT_PATH/nix-gitlab-ci"; - componentVersion = "$CI_COMMIT_SHORT_SHA"; - componentInputs.cache_files = ["flake.*" "nix/repo/ci.nix"]; - # bootstrapping still needs to be done in the gitlab-ci.yml directly, - # the child pipeline can then use the built images to test them - extraData = { - stages = ["build-images" "build" "trigger"]; - "build:image" = { - stage = "build-images"; - parallel.matrix = [ - {ARCH = ["x86_64-linux" "aarch64-linux"];} - ]; - image = "nixpkgs/nix-flakes:latest"; - script = ["nix build .#image --system $ARCH"]; - after_script = ["install -D result dist/nix-ci-$ARCH.tar.gz"]; - artifacts.paths = ["dist"]; - }; - "deploy:image" = { - stage = "build-images"; - image = "nixpkgs/nix-flakes:latest"; - needs = ["build:image"]; - before_script = [ - # sh - '' - nix profile install nixpkgs#buildah - export PATH="$PATH:$HOME/.nix-profile/bin" - export REGISTRY_AUTH_FILE=''${HOME}/auth.json - echo "$CI_REGISTRY_PASSWORD" | buildah login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY - mkdir -p /etc/containers && echo '{"default":[{"type":"insecureAcceptAnything"}]}' > /etc/containers/policy.json - mkdir -p /var/tmp - '' - ]; - script = [ - # sh - '' - export NORMALIZED_BRANCH=''${CI_COMMIT_BRANCH/\//-} - buildah manifest create localhost/nix-ci - buildah manifest add localhost/nix-ci docker-archive:dist/nix-ci-x86_64-linux.tar.gz - buildah manifest add localhost/nix-ci docker-archive:dist/nix-ci-aarch64-linux.tar.gz - buildah manifest push --all localhost/nix-ci docker://''${CI_REGISTRY_IMAGE}/nix-ci:''${CI_COMMIT_SHORT_SHA} - # branches - if [ -z "$CI_COMMIT_TAG" ]; then - buildah manifest push --all localhost/nix-ci docker://''${CI_REGISTRY_IMAGE}/nix-ci:''${NORMALIZED_BRANCH/main/latest} - fi - # tags - if [ -n "$CI_COMMIT_TAG" ]; then - buildah manifest push --all localhost/nix-ci docker://''${CI_REGISTRY_IMAGE}/nix-ci:''${CI_COMMIT_TAG} - fi - '' - ]; - }; - }; - }; - pipelines."default" = { - stages = ["test" "build" "deploy"]; - jobs = { - "test" = { - stage = "test"; - script = [ - "nix run .#tests -- --junit=junit.xml" - ]; - allow_failure = true; - artifacts = { - when = "always"; - reports.junit = "junit.xml"; - }; - }; - "docs" = { - stage = "build"; - script = [ - # sh - '' - nix build .#docs:default - mkdir -p public - cp -r result/. public/ - '' - ]; - artifacts.paths = ["public"]; - }; - "pages" = { - nix.enable = false; - image = "alpine:latest"; - stage = "deploy"; - script = ["true"]; - artifacts.paths = ["public"]; - rules = [ - { - "if" = "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"; - } - ]; - }; - }; - }; - } diff --git a/nix/repo/devShells.nix b/nix/repo/devShells.nix deleted file mode 100644 index 10889f9..0000000 --- a/nix/repo/devShells.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ - cell, - inputs, - ... -}: let - inherit (inputs) pkgs devshell treefmt soonix; - inherit (cell) ci; -in { - default = devshell.mkShell { - imports = [soonix.devshellModule]; - packages = [ - pkgs.nil - (treefmt.mkWrapper pkgs { - programs = { - alejandra.enable = true; - deadnix.enable = true; - statix.enable = true; - mdformat.enable = true; - yamlfmt.enable = true; - }; - settings.formatter = { - yamlfmt.excludes = ["templates/nix-gitlab-ci.yml" ".gitlab-ci.yml"]; - mdformat.command = let - pkg = pkgs.python3.withPackages (p: [ - p.mdformat - p.mdformat-mkdocs - ]); - in "${pkg}/bin/mdformat"; - }; - }) - ]; - - soonix.hooks."ci" = ci.soonix; - }; -} diff --git a/nix/repo/docs.nix b/nix/repo/docs.nix deleted file mode 100644 index 052b177..0000000 --- a/nix/repo/docs.nix +++ /dev/null @@ -1,80 +0,0 @@ -{inputs, ...}: let - inherit (inputs) pkgs cilib doclib; - - optionsDoc = doclib.mkOptionDocs { - module = cilib.modules.nixCiSubmodule; - roots = [ - { - url = "https://gitlab.com/TECHNOFAB/nix-gitlab-ci/-/blob/main/lib"; - path = "${inputs.self}/lib"; - } - ]; - }; - optionsDocs = pkgs.runCommand "options-docs" {} '' - mkdir -p $out - ln -s ${optionsDoc} $out/options.md - ''; -in - (doclib.mkDocs { - docs."default" = { - base = "${inputs.self}"; - path = "${inputs.self}/docs"; - material = { - enable = true; - colors = { - primary = "deep orange"; - accent = "orange"; - }; - umami = { - enable = true; - src = "https://analytics.tf/umami"; - siteId = "28f7c904-db22-4c2b-9ee4-ed42e14b6db9"; - domains = ["nix-gitlab-ci.projects.tf"]; - }; - }; - macros = { - enable = true; - includeDir = toString optionsDocs; - }; - config = { - site_name = "Nix-GitLab-CI"; - site_url = "https://nix-gitlab-ci.projects.tf"; - repo_name = "TECHNOFAB/nix-gitlab-ci"; - repo_url = "https://gitlab.com/TECHNOFAB/nix-gitlab-ci"; - extra_css = ["style.css"]; - theme = { - logo = "images/logo.svg"; - icon.repo = "simple/gitlab"; - favicon = "images/logo.svg"; - }; - nav = [ - {"Introduction" = "index.md";} - {"Setup" = "setup.md";} - {"Usage" = "usage.md";} - {"CI/CD Component" = "cicd_component.md";} - {"Environment Variables" = "environment_variables.md";} - {"Caching" = "caching.md";} - {"Multiple Pipelines" = "multi_pipeline.md";} - {"Soonix Integration" = "soonix.md";} - {"Utilities" = "utilities.md";} - {"Kubernetes Runner Example" = "kubernetes_runner.md";} - {"Example Configs" = "examples.md";} - {"Options" = "options.md";} - ]; - markdown_extensions = [ - { - "pymdownx.highlight".pygments_lang_class = true; - } - "pymdownx.inlinehilite" - "pymdownx.snippets" - "pymdownx.superfences" - "pymdownx.escapeall" - "fenced_code" - "admonition" - ]; - }; - }; - }).packages - // { - inherit optionsDocs; - } diff --git a/nix/repo/flake.lock b/nix/repo/flake.lock deleted file mode 100644 index 0627f8b..0000000 --- a/nix/repo/flake.lock +++ /dev/null @@ -1,99 +0,0 @@ -{ - "nodes": { - "devshell-lib": { - "locked": { - "dir": "lib", - "lastModified": 1755673398, - "narHash": "sha256-51MmR+Eo1+bKDd/Ss77wwTqi4yAR2xgmyCSEbKWSpj0=", - "owner": "rensa-nix", - "repo": "devshell", - "rev": "e76bef387e8a4574f9b6d37b1a424e706491af08", - "type": "gitlab" - }, - "original": { - "dir": "lib", - "owner": "rensa-nix", - "repo": "devshell", - "type": "gitlab" - } - }, - "nixmkdocs-lib": { - "locked": { - "dir": "lib", - "lastModified": 1757055638, - "narHash": "sha256-KHYSkEreFe4meXzSdEbknC/HwaQSNClQkc8vzHlAsMM=", - "owner": "TECHNOFAB", - "repo": "nixmkdocs", - "rev": "7840a5febdbeaf2da90babf6c94b3d0929d2bf74", - "type": "gitlab" - }, - "original": { - "dir": "lib", - "owner": "TECHNOFAB", - "repo": "nixmkdocs", - "type": "gitlab" - } - }, - "nixtest-lib": { - "locked": { - "dir": "lib", - "lastModified": 1756812148, - "narHash": "sha256-0g8KNk4zoLApA51PBHOWqPLRYpprjrQuSzNCjfBQgu8=", - "owner": "TECHNOFAB", - "repo": "nixtest", - "rev": "5741109cc9ec2b6d41b56abd3f5bc51ed7a9a228", - "type": "gitlab" - }, - "original": { - "dir": "lib", - "owner": "TECHNOFAB", - "repo": "nixtest", - "type": "gitlab" - } - }, - "root": { - "inputs": { - "devshell-lib": "devshell-lib", - "nixmkdocs-lib": "nixmkdocs-lib", - "nixtest-lib": "nixtest-lib", - "soonix-lib": "soonix-lib", - "treefmt-nix": "treefmt-nix" - } - }, - "soonix-lib": { - "locked": { - "dir": "lib", - "lastModified": 1756797658, - "narHash": "sha256-4rkyP4oaoqG/FFVL7W8U+8hGer4tOBPff/2SeN5tJYQ=", - "owner": "TECHNOFAB", - "repo": "soonix", - "rev": "3baef660cf8b87391d475a0455dd66fae0e60008", - "type": "gitlab" - }, - "original": { - "dir": "lib", - "owner": "TECHNOFAB", - "repo": "soonix", - "type": "gitlab" - } - }, - "treefmt-nix": { - "flake": false, - "locked": { - "lastModified": 1756662192, - "narHash": "sha256-F1oFfV51AE259I85av+MAia221XwMHCOtZCMcZLK2Jk=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "1aabc6c05ccbcbf4a635fb7a90400e44282f61c4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/nix/repo/flake.nix b/nix/repo/flake.nix deleted file mode 100644 index 149ae74..0000000 --- a/nix/repo/flake.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ - inputs = { - devshell-lib.url = "gitlab:rensa-nix/devshell?dir=lib"; - nixtest-lib.url = "gitlab:TECHNOFAB/nixtest?dir=lib"; - soonix-lib.url = "gitlab:TECHNOFAB/soonix?dir=lib"; - nixmkdocs-lib.url = "gitlab:TECHNOFAB/nixmkdocs?dir=lib"; - treefmt-nix = { - url = "github:numtide/treefmt-nix"; - flake = false; - }; - }; - outputs = i: - i - // { - devshell = i.devshell-lib.lib {inherit (i.parent) pkgs;}; - soonix = i.soonix-lib.lib {inherit (i.parent) pkgs;}; - ntlib = i.nixtest-lib.lib {inherit (i.parent) pkgs;}; - doclib = i.nixmkdocs-lib.lib {inherit (i.parent) pkgs;}; - cilib = import "${i.parent.self}/lib" {inherit (i.parent) pkgs;}; - treefmt = import i.treefmt-nix; - }; -} diff --git a/nix/repo/tests.nix b/nix/repo/tests.nix deleted file mode 100644 index 62c0833..0000000 --- a/nix/repo/tests.nix +++ /dev/null @@ -1,10 +0,0 @@ -{inputs, ...}: let - inherit (inputs) pkgs ntlib cilib; -in { - tests = ntlib.mkNixtest { - modules = ntlib.autodiscover {dir = "${inputs.self}/tests";}; - args = { - inherit pkgs ntlib cilib; - }; - }; -} diff --git a/nix/packages/scripts/finalize_nix_ci.sh b/scripts/finalize_nix_ci.sh similarity index 99% rename from nix/packages/scripts/finalize_nix_ci.sh rename to scripts/finalize_nix_ci.sh index ecc2211..baa6a0d 100644 --- a/nix/packages/scripts/finalize_nix_ci.sh +++ b/scripts/finalize_nix_ci.sh @@ -38,3 +38,4 @@ echo -e "\\e[0Ksection_start:`date +%s`:finalize_nix_ci[collapsed=true]\\r\\e[0K echo "Caching disabled, not uploading $COUNT new store entries..." fi echo -e "\\e[0Ksection_end:`date +%s`:finalize_nix_ci\\r\\e[0K" + diff --git a/nix/packages/scripts/setup_nix_ci.sh b/scripts/setup_nix_ci.sh similarity index 99% rename from nix/packages/scripts/setup_nix_ci.sh rename to scripts/setup_nix_ci.sh index eaf189b..1f2af66 100644 --- a/nix/packages/scripts/setup_nix_ci.sh +++ b/scripts/setup_nix_ci.sh @@ -46,3 +46,4 @@ echo -e "\\e[0Ksection_start:`date +%s`:nix_setup[collapsed=true]\\r\\e[0KSettin echo -e "\\e[0Ksection_end:`date +%s`:nix_deps\\r\\e[0K" fi echo -e "\\e[0Ksection_end:`date +%s`:nix_setup\\r\\e[0K" + diff --git a/snapshots/default.snap.json b/snapshots/default.snap.json new file mode 100644 index 0000000..c247a78 --- /dev/null +++ b/snapshots/default.snap.json @@ -0,0 +1 @@ +{"docs":{"after_script":["finalize_nix_ci"],"artifacts":{"paths":["public"]},"before_script":["source setup_nix_ci \"gitlab-ci:pipeline:default:job-deps:docs\""],"image":"$NIX_CI_IMAGE","script":["nix build .#docs:default\nmkdir -p public\ncp -r result/. public/\n"],"stage":"build"},"nixtest":{"after_script":["finalize_nix_ci"],"allow_failure":true,"artifacts":{"reports":{"junit":"junit.xml"},"when":"always"},"before_script":["source setup_nix_ci \"gitlab-ci:pipeline:default:job-deps:nixtest\""],"image":"$NIX_CI_IMAGE","script":["nix run .#nixtests:run -- --junit=junit.xml --pure"],"stage":"nixtest"},"pages":{"artifacts":{"paths":["public"]},"image":"alpine:latest","rules":[{"if":"$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"}],"script":["true"],"stage":"deploy"},"stages":["test","nixtest","build","deploy"],"test":{"after_script":["finalize_nix_ci"],"before_script":["source setup_nix_ci \"gitlab-ci:pipeline:default:job-deps:test\""],"cache":[{"key":"$CI_JOB_NAME-$CI_COMMIT_REF_SLUG","paths":[".nix-cache/"]}],"image":"$NIX_CI_IMAGE","script":["hello","curl google.de","echo $TEST $TEST_WITH_DERIVATION"],"stage":"test","variables":{"NIX_CI_CACHE_STRATEGY":"runner","TEST":"test"}},"test-default":{"after_script":["finalize_nix_ci"],"before_script":["source setup_nix_ci \"gitlab-ci:pipeline:default:job-deps:test-default\""],"image":"$NIX_CI_IMAGE","script":["hello"],"stage":"test"},"test-non-nix":{"image":"alpine:latest","script":["echo \"This job will not be modified to use nix\""],"stage":"test"}} \ No newline at end of file diff --git a/snapshots/non-default.snap.json b/snapshots/non-default.snap.json new file mode 100644 index 0000000..9ed3256 --- /dev/null +++ b/snapshots/non-default.snap.json @@ -0,0 +1 @@ +{"stages":["test"],"test":{"after_script":["finalize_nix_ci"],"before_script":["source setup_nix_ci \"gitlab-ci:pipeline:non-default:job-deps:test\""],"image":"$NIX_CI_IMAGE","script":["echo Hello from another pipeline"],"stage":"test"}} \ No newline at end of file diff --git a/templates/nix-gitlab-ci.yml b/templates/nix-gitlab-ci.yml index 14eeaa9..2dcead3 100644 --- a/templates/nix-gitlab-ci.yml +++ b/templates/nix-gitlab-ci.yml @@ -110,4 +110,3 @@ nix-ci:trigger: strategy: depend forward: pipeline_variables: true - diff --git a/tests/ci-lib.nix b/tests/ci-lib.nix new file mode 100644 index 0000000..577139c --- /dev/null +++ b/tests/ci-lib.nix @@ -0,0 +1,140 @@ +{ + lib, + pkgs, + ... +}: let + cilib = import ./../lib {inherit lib pkgs;}; +in { + nixtest.suites."CI Lib" = { + pos = __curPos; + tests = let + inherit (cilib.jobDeps) mkJobDeps; + inherit (cilib.jobRun) mkJobRun; + inherit (cilib.jobPatch) mkJobPatched; + inherit (cilib.pipeline) mkPipeline; + deps = mkJobDeps { + key = "test"; + job = { + nix.deps = [pkgs.hello]; + variables.TEST = "${pkgs.curl}"; + }; + }; + in [ + { + name = "jobDeps"; + type = "script"; + script = + # sh + '' + export PATH=${lib.makeBinPath [pkgs.gnugrep]} + grep -q "/nix/store" ${deps} + grep -q 'hello/bin:$PATH' ${deps} + grep -q "export TEST=" ${deps} + grep -q "curl" ${deps} + ''; + } + { + name = "jobRun"; + type = "script"; + script = let + run = mkJobRun { + key = "test"; + job.script = ["hello"]; + jobDeps = deps; + }; + in + # sh + '' + export PATH=${lib.makeBinPath [pkgs.gnugrep]} + grep -q "sandbox-helper" ${run}/bin/gitlab-ci-job:test + grep -q "gitlab-ci-job-test-raw" ${run}/bin/gitlab-ci-job:test + grep -q "gitlab-ci-job-deps-test" ${run.passthru.actualJobScript} + grep -q "Running script..." ${run.passthru.actualJobScript} + grep -q "hello" ${run.passthru.actualJobScript} + ''; + } + { + name = "jobPatched nix disabled"; + expected = {}; + actual = mkJobPatched { + key = "test"; + pipeline_name = "test"; + job.nix.enable = false; + }; + } + { + name = "jobPatched without runner cache"; + expected = { + after_script = ["finalize_nix_ci"]; + before_script = ["source setup_nix_ci \"gitlab-ci:pipeline:test:job-deps:test\""]; + }; + actual = mkJobPatched { + key = "test"; + pipeline_name = "test"; + job.nix = { + enable = true; + enable-runner-cache = false; + }; + }; + } + { + name = "jobPatched with runner cache"; + expected = { + after_script = ["finalize_nix_ci"]; + before_script = ["source setup_nix_ci \"gitlab-ci:pipeline:test:job-deps:test\""]; + cache = [ + { + key = "test"; + paths = [".nix-cache/"]; + } + ]; + variables."NIX_CI_CACHE_STRATEGY" = "runner"; + }; + actual = mkJobPatched { + key = "test"; + pipeline_name = "test"; + job.nix = { + enable = true; + enable-runner-cache = true; + runner-cache-key = "test"; + }; + }; + } + { + name = "mkPipeline empty"; + expected = {}; + actual = + (mkPipeline { + name = "test"; + pipeline.jobs = {}; + }).finalConfig; + } + { + name = "mkPipeline empty packages"; + type = "script"; + script = let + pipeline = builtins.toFile "pipeline-test" (builtins.toJSON + (mkPipeline { + name = "test"; + pipeline.jobs = {}; + }).packages); + in + # sh + '' + set -euo pipefail + export PATH=${lib.makeBinPath [pkgs.jq pkgs.gnugrep pkgs.coreutils]} + # single key + jq 'keys | length == 1' "${pipeline}" | grep -q true + # key is exactly "gitlab-ci:pipeline:test" + jq -r 'keys[0]' "${pipeline}" | grep -qx "gitlab-ci:pipeline:test" + # value contains "/nix/store/" + jq -r '.["gitlab-ci:pipeline:test"]' "${pipeline}" | grep -q "/nix/store/" + # value contains "gitlab-ci-test.yml" + jq -r '.["gitlab-ci:pipeline:test"]' "${pipeline}" | grep -q "gitlab-ci-test.yml" + # file only contains "{}" + [[ "$(cat $(jq -r '.["gitlab-ci:pipeline:test"]' "${pipeline}"))" == "{}" ]] + ''; + } + ]; + }; +} diff --git a/tests/cilib_test.nix b/tests/cilib_test.nix deleted file mode 100644 index 78e99a3..0000000 --- a/tests/cilib_test.nix +++ /dev/null @@ -1,168 +0,0 @@ -{ - pkgs, - cilib, - ntlib, - ... -}: { - suites."CI Lib" = { - pos = __curPos; - tests = let - inherit (cilib) mkJobDeps mkJobRun mkJobPatched mkPipeline; - deps = mkJobDeps { - key = "test"; - job = { - variables.TEST = "${pkgs.curl}"; - }; - nixConfig = { - enable = true; - deps = [pkgs.hello]; - }; - }; - in [ - { - name = "jobDeps"; - type = "script"; - script = - # sh - '' - ${ntlib.helpers.path [pkgs.gnugrep]} - ${ntlib.helpers.scriptHelpers} - assert_file_contains ${deps} "/nix/store" "should contain nix store path" - assert_file_contains ${deps} '-hello-.*/bin:$PATH' "should contain hello" - assert_file_contains ${deps} "export TEST=" "should export TEST" - assert_file_contains ${deps} "curl" "should contain curl" - ''; - } - { - name = "jobRun"; - type = "script"; - script = let - run = mkJobRun { - key = "test"; - job.script = ["hello"]; - jobDeps = deps; - }; - in - # sh - '' - ${ntlib.helpers.path [pkgs.gnugrep]} - ${ntlib.helpers.scriptHelpers} - assert_file_contains ${run}/bin/gitlab-ci-job:test "sandbox-helper" "should contain sandbox-helper" - assert_file_contains ${run}/bin/gitlab-ci-job:test "gitlab-ci-job-test-raw" "should contain job name" - assert_file_contains ${run.passthru.actualJobScript} "gitlab-ci-job-deps-test" "should contain job name" - assert_file_contains ${run.passthru.actualJobScript} "Running script..." "should contain 'Running script...'" - assert_file_contains ${run.passthru.actualJobScript} "hello" "should contain hello" - ''; - } - { - name = "jobPatched nix disabled"; - expected = {}; - actual = mkJobPatched { - key = "test"; - pipelineName = "test"; - job = {}; - nixConfig.enable = false; - }; - } - { - name = "jobPatched without runner cache"; - expected = { - after_script = ["finalize_nix_ci"]; - before_script = ["source setup_nix_ci \"gitlab-ci:pipeline:test:job-deps:test\""]; - }; - actual = mkJobPatched { - key = "test"; - pipelineName = "test"; - job = {}; - nixConfig = { - enable = true; - enableRunnerCache = false; - }; - }; - } - { - name = "jobPatched with runner cache"; - expected = { - after_script = ["finalize_nix_ci"]; - before_script = ["source setup_nix_ci \"gitlab-ci:pipeline:test:job-deps:test\""]; - cache = [ - { - key = "test"; - paths = [".nix-cache/"]; - } - ]; - variables."NIX_CI_CACHE_STRATEGY" = "runner"; - }; - actual = mkJobPatched { - key = "test"; - pipelineName = "test"; - job = {}; - nixConfig = { - enable = true; - enableRunnerCache = true; - runnerCacheKey = "test"; - }; - }; - } - { - name = "mkPipeline empty"; - expected = {}; - actual = - (mkPipeline { - name = "test"; - nixConfig = {}; - pipeline.jobs = {}; - }).finalConfig; - } - { - name = "mkPipeline empty packages"; - type = "script"; - script = let - pipeline = - ntlib.helpers.toJsonFile - (mkPipeline { - name = "test"; - nixConfig = {}; - pipeline.jobs = {}; - }).packages; - in - # sh - '' - set -euo pipefail - ${ntlib.helpers.path [pkgs.jq pkgs.gnugrep pkgs.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'" - jq -r 'keys[0]' "${pipeline}" | grep -qx "gitlab-ci:pipeline:test" - echo "key[1] is exactly 'gitlab-ci:pipeline:test:pretty'" - jq -r 'keys[1]' "${pipeline}" | grep -qx "gitlab-ci:pipeline:test:pretty" - echo "value contains '/nix/store/'" - jq -r '.["gitlab-ci:pipeline:test"]' "${pipeline}" | grep -q "/nix/store/" - echo "value contains 'gitlab-ci-test.yml'" - jq -r '.["gitlab-ci:pipeline:test"]' "${pipeline}" | grep -q "gitlab-ci-test.yml" - echo "file only contains '{}'" - [[ "$(cat $(jq -r '.["gitlab-ci:pipeline:test"]' "${pipeline}"))" == "{}" ]] - ''; - } - { - name = "handle store paths in variables"; - expected = { - stages = ["test"]; - test.stage = "test"; - }; - actual = - (mkPipeline { - name = "test"; - nixConfig.enable = false; - pipeline = { - stages = ["test"]; - jobs.test = { - stage = "test"; - variables."TEST" = "${pkgs.hello}"; - }; - }; - }).finalConfig; - } - ]; - }; -} diff --git a/tests/default.nix b/tests/default.nix new file mode 100644 index 0000000..764ae46 --- /dev/null +++ b/tests/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./utils.nix + ./ci-lib.nix + ./helpers.nix + ./pipeline-yamls.nix + ]; +} diff --git a/tests/fixtures/flake_parts/flake.lock b/tests/fixtures/flake_parts/flake.lock deleted file mode 100644 index 15b9835..0000000 --- a/tests/fixtures/flake_parts/flake.lock +++ /dev/null @@ -1,77 +0,0 @@ -{ - "nodes": { - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1754487366, - "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1756636162, - "narHash": "sha256-mBecwgUTWRgClJYqcF+y4O1bY8PQHqeDpB+zsAn+/zA=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "37ff64b7108517f8b6ba5705ee5085eac636a249", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1753579242, - "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs", - "systems": "systems" - } - }, - "systems": { - "locked": { - "lastModified": 1689347949, - "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", - "owner": "nix-systems", - "repo": "default-linux", - "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default-linux", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/tests/fixtures/flake_parts/flake.nix b/tests/fixtures/flake_parts/flake.nix deleted file mode 100644 index cca696f..0000000 --- a/tests/fixtures/flake_parts/flake.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ - outputs = { - flake-parts, - systems, - ... - } @ inputs: - flake-parts.lib.mkFlake {inherit inputs;} { - imports = [ - "@repo_path@/lib/flakeModule.nix" - ]; - systems = import systems; - flake = {}; - perSystem = _: { - ci = { - config = { - # true is already default, just for testing - nixJobsByDefault = true; - }; - pipelines = { - "default" = { - stages = ["example"]; - jobs."example" = { - stage = "example"; - script = ["echo hello world"]; - }; - }; - "test".jobs."example" = { - stage = ".pre"; - script = ["echo hello world"]; - }; - }; - }; - }; - }; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; - systems.url = "github:nix-systems/default-linux"; - }; -} diff --git a/tests/flake_parts_test.nix b/tests/flake_parts_test.nix deleted file mode 100644 index e998f45..0000000 --- a/tests/flake_parts_test.nix +++ /dev/null @@ -1,38 +0,0 @@ -{ - pkgs, - ntlib, - ... -}: { - suites."flake-parts" = { - pos = __curPos; - tests = [ - { - name = "flakeModule"; - type = "script"; - script = - # sh - '' - ${ntlib.helpers.scriptHelpers} - ${ntlib.helpers.path (with pkgs; [coreutils nix gnused gnugrep jq])} - export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt - export NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt - repo_path=${../.} - - cp ${./fixtures/flake_parts}/* . - # import from the absolute path above, is easier than trying to figure out the repo path etc. - sed -i -e "s|@repo_path@|$repo_path|" flake.nix - - # NOTE: --impure is required since importing modules from absolute paths is not allowed in pure mode - nix build --impure .#gitlab-ci:pipeline:default - assert "-f result" "should exist" - assert_file_contains "result" "finalize_nix_ci" - jq '.' result # check if valid json just to be sure - - nix build --impure .#gitlab-ci:pipeline:default:pretty - assert "-f result" "should exist" - assert_file_contains "result" " - finalize_nix_ci" - ''; - } - ]; - }; -} diff --git a/tests/helpers_test.nix b/tests/helpers.nix similarity index 59% rename from tests/helpers_test.nix rename to tests/helpers.nix index 4d7e7d4..1d100fc 100644 --- a/tests/helpers_test.nix +++ b/tests/helpers.nix @@ -1,28 +1,60 @@ { + lib, pkgs, - cilib, ... -}: { - suites."Helpers" = { +}: let + cilib = import ./../lib {inherit lib pkgs;}; +in { + nixtest.suites."Helpers" = { pos = __curPos; tests = let inherit (cilib) helpers; in [ + { + name = "appendToAfterScript nix disabled"; + expected = {}; + actual = helpers.appendToAfterScript [] {}; + } + { + name = "appendToAfterScript empty"; + expected = { + nix.enable = true; + after_script = []; + }; + actual = helpers.appendToAfterScript [] {nix.enable = true;}; + } { name = "appendToAfterScript"; expected = { + nix.enable = true; after_script = ["echo after_script" "finalize_nix_ci"]; }; actual = helpers.appendToAfterScript ["finalize_nix_ci"] { + nix.enable = true; after_script = ["echo after_script"]; }; } + { + name = "prependToBeforeScript nix disabled"; + expected = {}; + actual = helpers.prependToBeforeScript [] {}; + } + { + name = "prependToBeforeScript empty"; + expected = { + nix.enable = true; + before_script = []; + }; + actual = helpers.prependToBeforeScript [] {nix.enable = true;}; + } { name = "prependToBeforeScript"; expected = { + nix.enable = true; before_script = ["setup_nix_ci" "echo before_script"]; }; actual = helpers.prependToBeforeScript ["setup_nix_ci"] { + nix.enable = true; before_script = ["echo before_script"]; }; } @@ -34,22 +66,18 @@ { name = "filterAttrsRec"; expected = {world = "world";}; - actual = helpers.filterAttrsRec (_n: v: v != null) { + actual = helpers.filterAttrsRec (n: v: v != null) { hello = null; world = "world"; }; } { name = "filterJobVariables with store paths"; - expected = { - HELLO = "${pkgs.hello}"; - MULTIPLE = "${pkgs.hello}:${pkgs.hello}"; - }; + expected = {HELLO = "${pkgs.hello}";}; actual = helpers.filterJobVariables true { variables = { HELLO = "${pkgs.hello}"; WORLD = "world"; - MULTIPLE = "${pkgs.hello}:${pkgs.hello}"; }; }; } diff --git a/tests/modules_test.nix b/tests/modules_test.nix deleted file mode 100644 index e134293..0000000 --- a/tests/modules_test.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ - pkgs, - cilib, - ntlib, - ... -}: { - suites."Modules" = { - pos = __curPos; - tests = let - simplePipeline = cilib.mkCI { - pipelines."test" = { - stages = ["test"]; - jobs."test" = { - stage = "test"; - script = ["echo hello world"]; - }; - }; - }; - in [ - { - name = "empty pipelines"; - expected = {}; - actual = - (cilib.mkCI {}).pipelines; - } - { - name = "empty packages"; - expected = {}; - actual = - (cilib.mkCI {}).packages; - } - { - name = "simple pipeline"; - expected = { - stages = [".pre" "test" ".post"]; - "test" = { - image = "$NIX_CI_IMAGE"; - stage = "test"; - before_script = ["source setup_nix_ci \"gitlab-ci:pipeline:test:job-deps:test\""]; - script = ["echo hello world"]; - after_script = ["finalize_nix_ci"]; - }; - }; - actual = simplePipeline.pipelines."test".finalConfig; - } - { - name = "simple pipeline yaml"; - type = "script"; - script = let - package = simplePipeline.packages."gitlab-ci:pipeline:test"; - in - # sh - '' - ${ntlib.helpers.path [pkgs.gnugrep]} - ${ntlib.helpers.scriptHelpers} - assert_file_contains ${package} 'gitlab-ci:pipeline:test:job-deps:test' - assert_file_contains ${package} 'finalize_nix_ci' - assert_file_contains ${package} 'echo hello world' - ''; - } - { - name = "dont fail on store paths"; - type = "script"; - script = let - package = - (cilib.mkCI { - pipelines."test" = { - variables.EXAMPLE = "${pkgs.hello}"; - }; - }).packages."gitlab-ci:pipeline:test"; - in - # sh - '' - ${ntlib.helpers.path [pkgs.gnugrep]} - ${ntlib.helpers.scriptHelpers} - assert_file_contains ${package} '[".pre",".post"]' - assert_file_contains ${package} '"EXAMPLE":"/nix/store/.*-hello-.*"' - ''; - } - ]; - }; -} diff --git a/tests/pipeline-yamls.nix b/tests/pipeline-yamls.nix new file mode 100644 index 0000000..32effbe --- /dev/null +++ b/tests/pipeline-yamls.nix @@ -0,0 +1,26 @@ +{ + lib, + pkgs, + self', + ... +}: let + cilib = import ./../lib {inherit lib pkgs;}; +in { + nixtest.suites."Pipeline YAMLs" = { + pos = __curPos; + tests = let + jsonFile = file: builtins.fromJSON (builtins.readFile file); + in [ + { + name = "default"; + type = "snapshot"; + actual = jsonFile self'.legacyPackages."gitlab-ci:pipeline:default"; + } + { + name = "non-default"; + type = "snapshot"; + actual = jsonFile self'.legacyPackages."gitlab-ci:pipeline:non-default"; + } + ]; + }; +} diff --git a/tests/soonix_test.nix b/tests/soonix_test.nix deleted file mode 100644 index c76edb4..0000000 --- a/tests/soonix_test.nix +++ /dev/null @@ -1,77 +0,0 @@ -{ - lib, - cilib, - ... -}: let - inherit (lib) trimWith; -in { - suites."Soonix" = { - pos = __curPos; - tests = let - version = trimWith { - start = true; - end = true; - } (builtins.readFile ../lib/VERSION); - in [ - { - name = "default soonix config"; - expected = { - data.include = [ - { - component = "gitlab.com/TECHNOFAB/nix-gitlab-ci/nix-gitlab-ci@${version}"; - inputs.version = version; - } - ]; - generator = "nix"; - opts.format = "yaml"; - hook = { - mode = "copy"; - gitignore = false; - }; - output = ".gitlab-ci.yml"; - }; - actual = - (cilib.mkCI { - config.soonix = {}; - }).soonix; - } - { - name = "custom soonix config"; - expected = { - data = { - include = [ - { - component = "gitlab.com/example/nix-gitlab-ci/nix-gitlab-ci@abc"; - inputs = { - version = "abc"; - hello = "world"; - }; - } - ]; - "example".script = ["hello"]; - }; - generator = "nix"; - opts.format = "yaml"; - hook = { - mode = "copy"; - gitignore = false; - }; - output = ".gitlab-ci.yml"; - }; - actual = - (cilib.mkCI { - config.soonix = { - componentVersion = "abc"; - componentUrl = "gitlab.com/example/nix-gitlab-ci/nix-gitlab-ci"; - componentInputs = { - hello = "world"; - }; - extraData = { - "example".script = ["hello"]; - }; - }; - }).soonix; - } - ]; - }; -} diff --git a/tests/utils.nix b/tests/utils.nix new file mode 100644 index 0000000..8d9591f --- /dev/null +++ b/tests/utils.nix @@ -0,0 +1,36 @@ +{ + lib, + pkgs, + ... +}: let + cilib = import ./../lib {inherit lib pkgs;}; +in { + nixtest.suites."Utils" = { + pos = __curPos; + tests = [ + { + name = "commitAndPushFiles"; + type = "script"; + script = let + inherit (cilib) utils; + job = builtins.toFile "test" ( + builtins.unsafeDiscardStringContext ( + builtins.toJSON ( + utils.commitAndPushFiles { + message = "hello world"; + files = ["a.md" "b.txt"]; + } {} + ) + ) + ); + in + # sh + '' + export PATH=${lib.makeBinPath [pkgs.gnugrep]} + grep -q 'git commit -m \\"hello world\\"' ${job} + grep -q 'git add a.md b.txt' ${job} + ''; + } + ]; + }; +} diff --git a/tests/utils_test.nix b/tests/utils_test.nix deleted file mode 100644 index 2e319b1..0000000 --- a/tests/utils_test.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ - pkgs, - ntlib, - cilib, - ... -}: { - suites."Utils" = { - pos = __curPos; - tests = [ - { - name = "commitAndPushFiles"; - type = "script"; - script = let - inherit (cilib) utils; - job = ntlib.helpers.toJsonFile ( - utils.commitAndPushFiles { - message = "hello world"; - files = ["a.md" "b.txt"]; - } {} - ); - in - # sh - '' - ${ntlib.helpers.path [pkgs.gnugrep]} - ${ntlib.helpers.scriptHelpers} - assert_file_contains ${job} 'git commit -m \\"hello world\\"' - assert_file_contains ${job} 'git add a.md b.txt' - ''; - } - ]; - }; -}