Skip to content

Commit 45b341b

Browse files
authored
chore(nix): migrate pre-commit runner to prek (#957)
## Summary - Swap the Python `pre-commit` binary for `prek` (Rust rewrite, drop-in compatible) via git-hooks.nix's `package` option - Shim `prek install` calls to inject `--git-dir` and `--overwrite` so hook install works in git worktrees and stays quiet on reinstall - Patch the generated shellHook to `--unset core.hooksPath` instead of setting it to `""`, which prek 0.3.9+ rejects - `run-pre-commit` app now invokes `prek run`; CI (`nix run '.#pre-commit'`) picks up the change transparently
1 parent 10d8e13 commit 45b341b

1 file changed

Lines changed: 136 additions & 89 deletions

File tree

flake.nix

Lines changed: 136 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -50,95 +50,142 @@
5050
text = builtins.readFile ./nix/scripts/license-check.bash;
5151
};
5252

53-
preCommit = git-hooks.lib.${system}.run {
54-
src = ./.;
55-
hooks = {
56-
golines = {
57-
enable = true;
58-
settings.flags = "--base-formatter=${lib.getExe pkgs.gofumpt} " + "--max-len=100";
59-
};
60-
nixfmt.enable = true;
61-
golangci-lint = {
62-
enable = false; # CI-only job
63-
stages = [ "pre-push" ];
64-
};
65-
actionlint.enable = true;
66-
statix.enable = true;
67-
deadnix.enable = true;
68-
biome = {
69-
enable = true;
70-
excludes = [ "\\.claude/settings\\.json" ];
71-
};
72-
taplo.enable = true;
73-
license-header = {
74-
enable = true;
75-
name = "license-header";
76-
entry = "${lib.getExe licenseCheck}";
77-
files = "\\.(go|nix|ya?ml|sh|md|js)$|^\\.envrc$|\\.gitignore$|^go\\.mod$";
78-
excludes = [
79-
"LICENSE"
80-
"flake\\.lock"
81-
"go\\.sum"
82-
"\\.json$"
83-
"^docs/content/"
84-
];
85-
language = "system";
86-
pass_filenames = true;
87-
};
88-
go-mod-tidy = {
89-
enable = true;
90-
name = "go-mod-tidy";
91-
entry = "${lib.getExe goModTidyCheck}";
92-
files = "\\.go$|^go\\.(mod|sum)$";
93-
language = "system";
94-
pass_filenames = false;
95-
};
96-
deadcode-check = {
97-
enable = false; # CI-only job
98-
name = "deadcode";
99-
entry = "${lib.getExe pkgs.deadcode}";
100-
files = "\\.go$";
101-
language = "system";
102-
pass_filenames = false;
103-
stages = [ "pre-push" ];
104-
};
105-
govulncheck = {
106-
enable = false; # CI-only job
107-
name = "govulncheck";
108-
entry = "${lib.getExe pkgs.govulncheck}";
109-
files = "^go\\.(mod|sum)$";
110-
language = "system";
111-
pass_filenames = false;
112-
stages = [ "pre-push" ];
113-
};
114-
osv-scanner = {
115-
enable = false; # CI-only job
116-
name = "osv-scanner";
117-
entry = "${lib.getExe pkgs.osv-scanner}";
118-
files = "^go\\.(mod|sum)$";
119-
language = "system";
120-
pass_filenames = false;
121-
stages = [ "pre-push" ];
122-
};
123-
go-generate-check = {
124-
enable = true;
125-
name = "go-generate-check";
126-
entry = "${lib.getExe goGenerateCheck}";
127-
files = "^(cmd/micasa/.*\\.go|internal/(data/(models|cmd/genmeta/main)|app/(coldefs|cmd/gencolumns/main)|config/.*)\\.go|docs/data/deprecations\\.json|docs/content/docs/reference/cli\\.md)$";
128-
language = "system";
129-
pass_filenames = false;
130-
stages = [ "pre-push" ];
131-
};
132-
vendor-hash-check = {
133-
enable = true;
134-
name = "vendor-hash-check";
135-
entry = "${lib.getExe vendorHashCheck}";
136-
files = "^go\\.(mod|sum)$";
137-
language = "system";
138-
pass_filenames = false;
53+
preCommit =
54+
let
55+
raw = git-hooks.lib.${system}.run {
56+
src = ./.;
57+
package = pkgs.prek;
58+
hooks = {
59+
golines = {
60+
enable = true;
61+
settings.flags = "--base-formatter=${lib.getExe pkgs.gofumpt} " + "--max-len=100";
62+
};
63+
nixfmt.enable = true;
64+
golangci-lint = {
65+
enable = false; # CI-only job
66+
stages = [ "pre-push" ];
67+
};
68+
actionlint.enable = true;
69+
statix.enable = true;
70+
deadnix.enable = true;
71+
biome = {
72+
enable = true;
73+
excludes = [ "\\.claude/settings\\.json" ];
74+
};
75+
taplo.enable = true;
76+
license-header = {
77+
enable = true;
78+
name = "license-header";
79+
entry = "${lib.getExe licenseCheck}";
80+
files = "\\.(go|nix|ya?ml|sh|md|js)$|^\\.envrc$|\\.gitignore$|^go\\.mod$";
81+
excludes = [
82+
"LICENSE"
83+
"flake\\.lock"
84+
"go\\.sum"
85+
"\\.json$"
86+
"^docs/content/"
87+
];
88+
language = "system";
89+
pass_filenames = true;
90+
};
91+
go-mod-tidy = {
92+
enable = true;
93+
name = "go-mod-tidy";
94+
entry = "${lib.getExe goModTidyCheck}";
95+
files = "\\.go$|^go\\.(mod|sum)$";
96+
language = "system";
97+
pass_filenames = false;
98+
};
99+
deadcode-check = {
100+
enable = false; # CI-only job
101+
name = "deadcode";
102+
entry = "${lib.getExe pkgs.deadcode}";
103+
files = "\\.go$";
104+
language = "system";
105+
pass_filenames = false;
106+
stages = [ "pre-push" ];
107+
};
108+
govulncheck = {
109+
enable = false; # CI-only job
110+
name = "govulncheck";
111+
entry = "${lib.getExe pkgs.govulncheck}";
112+
files = "^go\\.(mod|sum)$";
113+
language = "system";
114+
pass_filenames = false;
115+
stages = [ "pre-push" ];
116+
};
117+
osv-scanner = {
118+
enable = false; # CI-only job
119+
name = "osv-scanner";
120+
entry = "${lib.getExe pkgs.osv-scanner}";
121+
files = "^go\\.(mod|sum)$";
122+
language = "system";
123+
pass_filenames = false;
124+
stages = [ "pre-push" ];
125+
};
126+
go-generate-check = {
127+
enable = true;
128+
name = "go-generate-check";
129+
entry = "${lib.getExe goGenerateCheck}";
130+
files = "^(cmd/micasa/.*\\.go|internal/(data/(models|cmd/genmeta/main)|app/(coldefs|cmd/gencolumns/main)|config/.*)\\.go|docs/data/deprecations\\.json|docs/content/docs/reference/cli\\.md)$";
131+
language = "system";
132+
pass_filenames = false;
133+
stages = [ "pre-push" ];
134+
};
135+
vendor-hash-check = {
136+
enable = true;
137+
name = "vendor-hash-check";
138+
entry = "${lib.getExe vendorHashCheck}";
139+
files = "^go\\.(mod|sum)$";
140+
language = "system";
141+
pass_filenames = false;
142+
};
143+
};
139144
};
145+
# Upstream git-hooks.nix hardcodes `prek install` without --git-dir
146+
# or --overwrite, which respectively breaks in git worktrees (.git is
147+
# a file) and triggers "migration mode" warnings on every reinstall.
148+
# Shim install calls to inject both. prek <= 0.3.8 rejects --git-dir
149+
# on `uninstall`, so only install gets the flag; uninstall becomes a
150+
# best-effort no-op and the subsequent install --overwrite supersedes
151+
# any stale hook.
152+
prekShim = ''
153+
_prek_cmd() {
154+
local subcmd=$1
155+
shift
156+
local _prek_gitdir
157+
if [ "$subcmd" = install ] \
158+
&& _prek_gitdir=$(${lib.getExe pkgs.gitMinimal} rev-parse --path-format=absolute --git-common-dir 2>/dev/null); then
159+
${lib.getExe pkgs.prek} "$subcmd" --overwrite --git-dir "$_prek_gitdir" "$@"
160+
else
161+
${lib.getExe pkgs.prek} "$subcmd" "$@" 2>/dev/null || true
162+
fi
163+
}
164+
'';
165+
# prek 0.3.9+ rejects empty core.hooksPath (git resolves "" to ".");
166+
# upstream sets it to "" before reinstalling. Patch to --unset.
167+
patchedShellHook =
168+
builtins.replaceStrings
169+
[
170+
''core.hooksPath ""''
171+
(lib.getExe pkgs.prek)
172+
]
173+
[
174+
"--unset core.hooksPath 2>/dev/null || true #"
175+
"_prek_cmd"
176+
]
177+
raw.shellHook;
178+
ensureInstall = ''
179+
if ${lib.getExe pkgs.gitMinimal} rev-parse --git-dir >/dev/null 2>&1; then
180+
_prek_cmd install -c .pre-commit-config.yaml -t pre-commit >/dev/null
181+
_prek_cmd install -c .pre-commit-config.yaml -t pre-push >/dev/null
182+
fi
183+
'';
184+
in
185+
raw
186+
// {
187+
shellHook = prekShim + patchedShellHook + ensureInstall;
140188
};
141-
};
142189

143190
# Fontconfig for VHS recordings using Hack Nerd Font.
144191
# JetBrains Mono's variable font files cause xterm.js in Chromium to
@@ -460,8 +507,8 @@
460507
if [ $# -eq 0 ]; then
461508
set -- --all-files
462509
fi
463-
pre-commit run "$@"
464-
pre-commit run "$@" --hook-stage pre-push
510+
prek run "$@"
511+
prek run "$@" --hook-stage pre-push
465512
'';
466513
};
467514

0 commit comments

Comments
 (0)