feat: split lib and utils into separate flake

This commit is contained in:
TECHNOFAB 2024-07-28 12:51:14 +00:00
parent c272bfd812
commit f361878e13
5 changed files with 26 additions and 39 deletions

9
lib/flake.nix Normal file
View file

@ -0,0 +1,9 @@
{
description = "Nix-CI lib";
outputs = {...} @ inputs:
{
flakeModule = import ./flakeModule.nix;
}
// (import ./utils.nix);
}

181
lib/flakeModule.nix Normal file
View file

@ -0,0 +1,181 @@
{
flake-parts-lib,
lib,
...
}: {
options.perSystem = flake-parts-lib.mkPerSystemOption (
{
config,
pkgs,
...
}: let
cfg = config.ci.config;
filterAttrsRec = pred: v:
if lib.isAttrs v
then lib.filterAttrs pred (lib.mapAttrs (path: filterAttrsRec pred) v)
else v;
subType = options: lib.types.submodule {inherit options;};
mkNullOption = type:
lib.mkOption {
default = null;
type = lib.types.nullOr type;
};
configType = with lib;
subType {
default-nix-image = mkOption {
type = types.str;
default = "registry.gitlab.com/technofab/nix-gitlab-ci/nix-ci:latest";
description = "The image to use on nix jobs";
};
nix-jobs-per-default = mkOption {
type = types.bool;
default = true;
description = "Handle jobs nix-based by default or via opt-in (in job set nix = true) if false";
};
};
jobType = with lib;
subType {
# nix ci opts
nix = mkOption {
type = types.bool;
default = cfg.nix-jobs-per-default;
};
deps = mkOption {
type = types.listOf types.package;
default = [];
};
# gitlab opts
script = mkOption {
type = types.listOf types.str;
default = [];
};
stage = mkOption {
type = types.str;
};
image = mkOption {
type = types.str;
default = cfg.default-nix-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.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);
};
in {
options = with lib; {
ci = mkOption {
type = 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 = {};
};
};
description = ''
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.packages = let
toYaml = (pkgs.formats.yaml {}).generate;
mapAttrs = cb: set: builtins.listToAttrs (builtins.map (key: cb key (builtins.getAttr key set)) (builtins.attrNames set));
prepend = key: arr: job:
job
// lib.optionalAttrs job.nix {
${key} =
arr
++ job.${key} or [];
};
prependToBeforeScript = prepend "before_script";
prependToAfterScript = prepend "after_script";
jobs = filterAttrsRec (n: v: v != null) config.ci.jobs;
rest = filterAttrsRec (n: v: v != null) (builtins.removeAttrs config.ci ["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
# we can support different architectures between runners (eg. the arch of the initial runner does not matter)
jobsMappedForDeps =
mapAttrs (key: job: {
name = "gitlab-ci-job-deps:${key}";
value = pkgs.writeShellScript "gitlab-ci-job-deps:${key}" ''
export PATH="${lib.makeBinPath job.deps}:$PATH";
'';
})
jobs;
# allows the user to directly run the script
jobsMappedForScript =
mapAttrs (key: job: {
name = "gitlab-ci-job:${key}";
value = pkgs.writeShellScriptBin "gitlab-ci-job:${key}" (lib.strings.concatLines (job.before_script or [] ++ job.script ++ job.after_script or []));
})
jobs;
# build the deps specific for this job before anything, this way the deps should be fetched from the cache
jobsPatched =
mapAttrs (key: job: {
name = key;
value = builtins.removeAttrs (
(prependToBeforeScript [
"source setup_nix_ci ${key}"
]
(prependToAfterScript [
"finalize_nix_ci"
]
job))
// lib.optionalAttrs job.nix {
image = job.image;
}
) ["nix" "deps"];
})
jobs;
in
{
gitlab-ci-config = toYaml "generated-gitlab-ci.yml" (rest // jobsPatched);
}
// jobsMappedForDeps
// jobsMappedForScript;
}
);
}

46
lib/utils.nix Normal file
View file

@ -0,0 +1,46 @@
{
mkUtils = {pkgs, ...}: {
commitAndPushFiles = {
message,
files ? [],
}: jobArgs:
jobArgs
// {
before_script =
(jobArgs.before_script or [])
++ [
''
echo -e "\\e[0Ksection_start:`date +%s`:commit_setup[collapsed=true]\\r\\e[0KSetting up commitAndPushFiles"
eval "$(ssh-agent -s)" >/dev/null;
mkdir -p ~/.ssh; touch ~/.ssh/known_hosts;
ssh-keyscan -t rsa $CI_SERVER_HOST >> ~/.ssh/known_hosts;
echo "$GIT_SSH_PRIV_KEY" | tr -d '\r' | ssh-add - >/dev/null;
git config --global user.email "$GIT_EMAIL" >/dev/null;
git config --global user.name "$GIT_NAME" >/dev/null;
export CI_PUSH_REPO=`echo $CI_REPOSITORY_URL | sed -e "s|.*@\(.*\)|git@\1|" -e "s|/|:|"`;
git remote rm origin && git remote add origin ''${CI_PUSH_REPO}
echo -e "\\e[0Ksection_end:`date +%s`:commit_setup\\r\\e[0K"
''
];
script = let
addScript =
if builtins.length files == 0
then ""
else "git add ${builtins.concatStringsSep " " files}";
in
(jobArgs.script or [])
++ [
''
echo -e "\\e[0Ksection_start:`date +%s`:commit[collapsed=true]\\r\\e[0KCommiting & pushing changes if necessary"
${addScript}
git diff --cached --exit-code >/dev/null &&
echo "Nothing to commit" ||
git commit -m "${message}" --no-verify;
git push --tags origin ''${GIT_SOURCE_REF:-HEAD}:''${GIT_TARGET_REF:-$CI_COMMIT_REF_NAME} -o ci.skip
echo -e "\\e[0Ksection_end:`date +%s`:commit\\r\\e[0K"
''
];
deps = (jobArgs.deps or []) ++ [pkgs.openssh pkgs.gitMinimal pkgs.gnused];
};
};
}