Something went wrong. Try again.
A small, fast, drop-in replacement for git-hooks.nix
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177{pkgs}: let inherit (pkgs) lib;
hookSpec = import ./hook-spec.nix {inherit lib;}; scriptGen = import ./script-gen.nix {inherit lib;}; tangledGen = import ./tangled-gen.nix {inherit lib;}; githubActionsGen = import ./github-actions-gen.nix {inherit lib;};
# built from the caller's own `pkgs`; flake consumers get one of these per # system via nixhooks.lib.<system>.presets (see flake.nix). presets = import ./presets.nix {inherit pkgs;};
runtimeLib = builtins.readFile ./hooks-runtime.sh; driverPreCommit = builtins.readFile ./drivers/pre-commit.sh; driverPrePush = builtins.readFile ./drivers/pre-push.sh; driverCommitMsg = builtins.readFile ./drivers/commit-msg.sh; driverRunHooks = builtins.readFile ./drivers/run-hooks.sh;
# lets install-hooks tell a nixhooks-installed hook and other hooks apart marker = "# managed-by: nixhooks -- do not edit directly";
genFunction = calls: '' nixhooks_generated_hooks() { ${ if calls == "" then " :" else calls } } '';
mkStageScript = name: driver: calls: pkgs.writeShellScriptBin name '' ${marker} set -euo pipefail ${runtimeLib} ${genFunction calls} ${driver} '';
mkHooks = { hooks, # attrset of name -> hook config, see lib/hook-spec.nix for fields. settings ? {}, # optional {tangled, githubActions, parallel}; see below. }: let tangled = settings.tangled or {}; # optional Tangled pipeline config, see lib/tangled-gen.nix for fields. githubActions = settings.githubActions or {}; # optional GitHub Actions workflow config, see lib/github-actions-gen.nix for fields. parallel = settings.parallel or false; # optionally determines if hooks are ran in parallel in assert lib.assertMsg (builtins.isBool parallel) "nixhooks: settings.parallel must be a bool"; let normalized = hookSpec.normalizeHooks hooks; # resolve the top-level switch + per-hook escape hatch into one # effective flag before codegen so lib/script-gen.nix only ever # reads an already-decided hook.parallel scheduled = lib.mapAttrs (_: h: h // {parallel = parallel && !h.serial;}) normalized; tangledCfg = tangledGen.normalizeTangled tangled; githubActionsCfg = githubActionsGen.normalizeGithubActions githubActions;
preCommitHook = mkStageScript "pre-commit-hook" driverPreCommit ( scriptGen.genStageCalls "pre-commit" scheduled ); prePushHook = mkStageScript "pre-push-hook" driverPrePush ( scriptGen.genStageCalls "pre-push" scheduled ); commitMsgHook = mkStageScript "commit-msg-hook" driverCommitMsg ( scriptGen.genStageCalls "commit-msg" scheduled ); runHooks = mkStageScript "run-hooks" driverRunHooks (scriptGen.genAllCalls scheduled);
installHooks = pkgs.writeShellScriptBin "install-hooks" '' set -euo pipefail
if ! git_dir=$(git rev-parse --git-dir 2>/dev/null); then echo "install-hooks: not inside a git repository" >&2 exit 1 fi
install_one() { local stage="$1" src="$2" local dest="$git_dir/hooks/$stage" if [[ -L "$dest" && "$(readlink "$dest")" == "$src" ]]; then return 0 fi if [[ -e "$dest" && ! -L "$dest" ]] && ! grep -q ${lib.escapeShellArg marker} "$dest" 2>/dev/null; then echo "install-hooks: $dest already exists and is not managed by nixhooks, refusing to overwrite" >&2 exit 1 fi mkdir -p "$git_dir/hooks" ln -sf "$src" "$dest" echo "install-hooks: installed $stage -> $src" }
install_one "pre-commit" "${preCommitHook}/bin/pre-commit-hook" install_one "pre-push" "${prePushHook}/bin/pre-push-hook" install_one "commit-msg" "${commitMsgHook}/bin/commit-msg-hook" '';
tangledPipeline = pkgs.writeText "hooks.yml" '' ${marker} ${tangledGen.mkPipelineText tangled} '';
genTangledPipeline = pkgs.writeShellScriptBin "gen-tangled-pipeline" '' set -euo pipefail
if ! git_root=$(git rev-parse --show-toplevel 2>/dev/null); then echo "gen-tangled-pipeline: not inside a git repository" >&2 exit 1 fi
dest="$git_root/.tangled/workflows/hooks.yml" if [[ -e "$dest" ]] && ! grep -q ${lib.escapeShellArg marker} "$dest" 2>/dev/null; then echo "gen-tangled-pipeline: $dest already exists and is not managed by nixhooks, refusing to overwrite" >&2 exit 1 fi
mkdir -p "$(dirname "$dest")" install -m 0644 ${tangledPipeline} "$dest" echo "gen-tangled-pipeline: wrote $dest" ''; githubActionsWorkflow = pkgs.writeText "hooks.yml" '' ${marker} ${githubActionsGen.mkWorkflowText githubActions} '';
genGithubActionsWorkflow = pkgs.writeShellScriptBin "gen-github-actions-workflow" '' set -euo pipefail
if ! git_root=$(git rev-parse --show-toplevel 2>/dev/null); then echo "gen-github-actions-workflow: not inside a git repository" >&2 exit 1 fi
dest="$git_root/.github/workflows/hooks.yml" if [[ -e "$dest" ]] && ! grep -q ${lib.escapeShellArg marker} "$dest" 2>/dev/null; then echo "gen-github-actions-workflow: $dest already exists and is not managed by nixhooks, refusing to overwrite" >&2 exit 1 fi
mkdir -p "$(dirname "$dest")" install -m 0644 ${githubActionsWorkflow} "$dest" echo "gen-github-actions-workflow: wrote $dest" '';
hookOutputs = { pre-commit-hook = preCommitHook; pre-push-hook = prePushHook; commit-msg-hook = commitMsgHook; run-hooks = runHooks; install-hooks = installHooks; } // lib.optionalAttrs tangledCfg.enable { tangled-pipeline = tangledPipeline; gen-tangled-pipeline = genTangledPipeline; } // lib.optionalAttrs githubActionsCfg.enable { github-actions-workflow = githubActionsWorkflow; gen-github-actions-workflow = genGithubActionsWorkflow; }; in hookOutputs // { apps = builtins.mapAttrs (_: drv: { type = "app"; program = lib.getExe drv; }) (builtins.removeAttrs hookOutputs ["tangled-pipeline" "github-actions-workflow"]); };in { inherit mkHooks presets; inherit (hookSpec) normalizeHooks normalizeHook defaultHook; inherit (tangledGen) normalizeTangled defaultTangled; inherit (githubActionsGen) normalizeGithubActions defaultGithubActions;}