diff --git a/lib/impl/helpers.nix b/lib/impl/helpers.nix index ebeeb46..66a42cf 100644 --- a/lib/impl/helpers.nix +++ b/lib/impl/helpers.nix @@ -2,7 +2,7 @@ pkgs, lib, } @ args: let - inherit (lib) types isAttrs filterAttrs mapAttrs mkOption mkOptionType isType literalExpression; + inherit (lib) types isAttrs filterAttrs mapAttrs mkOption mkOptionType isType literalExpression pipe; in rec { prepend = key: arr: job: { ${key} = arr ++ (job.${key} or []); @@ -15,7 +15,13 @@ in rec { # 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); + 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)); diff --git a/lib/impl/modules/pipeline.nix b/lib/impl/modules/pipeline.nix index 9d1e636..cfb6284 100644 --- a/lib/impl/modules/pipeline.nix +++ b/lib/impl/modules/pipeline.nix @@ -4,7 +4,7 @@ jobSubmodule, ... }: let - inherit (lib) mkOption types filterAttrs mergeAttrsList pipe mapAttrs; + inherit (lib) mkOption types filterAttrs mergeAttrsList mapAttrs; inherit (cilib.helpers) filterUnset mkUnsetOption toYaml toYamlPretty; pipelineConfigSubmodule = {rootConfig, ...}: { @@ -93,12 +93,7 @@ // mapAttrs (_name: value: value.finalConfig) config.jobs; packages = { - "gitlab-ci:pipeline:${name}" = pipe config.finalConfig [ - builtins.toJSON - builtins.unsafeDiscardOutputDependency - builtins.unsafeDiscardStringContext - (toYaml "gitlab-ci-config.json") - ]; + "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)); diff --git a/tests/cilib_test.nix b/tests/cilib_test.nix new file mode 100644 index 0000000..d37da49 --- /dev/null +++ b/tests/cilib_test.nix @@ -0,0 +1,171 @@ +{ + 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"; + variables."TEST" = "${pkgs.hello}"; + }; + }; + actual = + (mkPipeline { + name = "test"; + nixConfig.enable = false; + pipeline = { + stages = ["test"]; + jobs.test = { + stage = "test"; + variables."TEST" = "${pkgs.hello}"; + }; + }; + }).finalConfig; + } + ]; + }; +} diff --git a/tests/helpers_test.nix b/tests/helpers_test.nix new file mode 100644 index 0000000..4d7e7d4 --- /dev/null +++ b/tests/helpers_test.nix @@ -0,0 +1,68 @@ +{ + pkgs, + cilib, + ... +}: { + suites."Helpers" = { + pos = __curPos; + tests = let + inherit (cilib) helpers; + in [ + { + name = "appendToAfterScript"; + expected = { + after_script = ["echo after_script" "finalize_nix_ci"]; + }; + actual = helpers.appendToAfterScript ["finalize_nix_ci"] { + after_script = ["echo after_script"]; + }; + } + { + name = "prependToBeforeScript"; + expected = { + before_script = ["setup_nix_ci" "echo before_script"]; + }; + actual = helpers.prependToBeforeScript ["setup_nix_ci"] { + before_script = ["echo before_script"]; + }; + } + { + name = "toYaml"; + expected = ''{"hello":"world"}''; + actual = builtins.readFile (helpers.toYaml "test" {hello = "world";}); + } + { + name = "filterAttrsRec"; + expected = {world = "world";}; + 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}"; + }; + actual = helpers.filterJobVariables true { + variables = { + HELLO = "${pkgs.hello}"; + WORLD = "world"; + MULTIPLE = "${pkgs.hello}:${pkgs.hello}"; + }; + }; + } + { + name = "filterJobVariables without store paths"; + expected = {WORLD = "world";}; + actual = helpers.filterJobVariables false { + variables = { + HELLO = "${pkgs.hello}"; + WORLD = "world"; + }; + }; + } + ]; + }; +} diff --git a/tests/modules_test.nix b/tests/modules_test.nix new file mode 100644 index 0000000..0b467f4 --- /dev/null +++ b/tests/modules_test.nix @@ -0,0 +1,81 @@ +{ + 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" = { + 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/soonix_test.nix b/tests/soonix_test.nix new file mode 100644 index 0000000..c76edb4 --- /dev/null +++ b/tests/soonix_test.nix @@ -0,0 +1,77 @@ +{ + 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_test.nix b/tests/utils_test.nix new file mode 100644 index 0000000..2e319b1 --- /dev/null +++ b/tests/utils_test.nix @@ -0,0 +1,32 @@ +{ + 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' + ''; + } + ]; + }; +}