Files
nix-flake-lib/packages/repo-lib/lib/hooks.nix
2026-03-21 01:27:42 +01:00

117 lines
2.9 KiB
Nix

{
lib,
sanitizeName,
}:
let
hookStageFileArgs =
stage: passFilenames:
if !passFilenames then
""
else if stage == "pre-commit" then
" {staged_files}"
else if stage == "pre-push" then
" {push_files}"
else if stage == "commit-msg" then
" {1}"
else
throw "repo-lib: unsupported lefthook stage '${stage}'";
normalizeHookStage =
hookName: stage:
if
builtins.elem stage [
"pre-commit"
"pre-push"
"commit-msg"
]
then
stage
else
throw "repo-lib: hook '${hookName}' has unsupported stage '${stage}' for lefthook";
in
{
inherit hookStageFileArgs normalizeHookStage;
checkToLefthookConfig =
pkgs: name: rawCheck:
let
check = {
stage = "pre-commit";
passFilenames = false;
runtimeInputs = [ ];
}
// rawCheck;
wrapperName = "repo-lib-check-${sanitizeName name}";
wrapper = pkgs.writeShellApplication {
name = wrapperName;
runtimeInputs = check.runtimeInputs;
text = ''
set -euo pipefail
${check.command}
'';
};
in
if !(check ? command) then
throw "repo-lib: check '${name}' is missing 'command'"
else if
!(builtins.elem check.stage [
"pre-commit"
"pre-push"
])
then
throw "repo-lib: check '${name}' has unsupported stage '${check.stage}'"
else
lib.setAttrByPath [ check.stage "commands" name ] {
run = "${wrapper}/bin/${wrapperName}${hookStageFileArgs check.stage check.passFilenames}";
};
normalizeLefthookConfig =
label: raw: if builtins.isAttrs raw then raw else throw "repo-lib: ${label} must be an attrset";
hookToLefthookConfig =
name: hook:
let
supportedFields = [
"description"
"enable"
"entry"
"name"
"package"
"pass_filenames"
"stages"
];
unsupportedFields = builtins.filter (field: !(builtins.elem field supportedFields)) (
builtins.attrNames hook
);
stages = builtins.map (stage: normalizeHookStage name stage) (hook.stages or [ "pre-commit" ]);
passFilenames = hook.pass_filenames or false;
in
if unsupportedFields != [ ] then
throw ''
repo-lib: hook '${name}' uses unsupported fields for lefthook: ${lib.concatStringsSep ", " unsupportedFields}
''
else if !(hook ? entry) then
throw "repo-lib: hook '${name}' is missing 'entry'"
else
lib.foldl' lib.recursiveUpdate { } (
builtins.map (
stage:
lib.setAttrByPath [ stage "commands" name ] {
run = "${hook.entry}${hookStageFileArgs stage passFilenames}";
}
) stages
);
parallelHookStageConfig =
stage:
if
builtins.elem stage [
"pre-commit"
"pre-push"
]
then
lib.setAttrByPath [ stage "parallel" ] true
else
{ };
}