-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.pre-commit-config.yaml
More file actions
218 lines (196 loc) · 8.44 KB
/
Copy path.pre-commit-config.yaml
File metadata and controls
218 lines (196 loc) · 8.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# Pre-commit hooks — split into two stages for speed:
#
# pre-commit (runs on every `git commit`): formatters + cheap hygiene
# target: < 3 seconds wall time
# pre-push (runs only on `git push`): linters and slower checks
# target: < 30 seconds wall time
#
# Whole-flake validation (`nix flake check`) and the full linter sweep are
# NOT in either stage — they're CI's job (check-configurations, lint-check
# jobs run on every PR). Doing them locally on every commit was duplicating
# work and adding 30s–7min to every commit (nixpkgs-lint-community was
# pathologically slow on files with large embedded heredocs).
#
# Install both stages locally:
# pre-commit install --hook-type pre-commit --hook-type pre-push
#
# Run all hooks manually:
# pre-commit run --all-files # pre-commit stage
# pre-commit run --all-files --hook-stage pre-push # pre-push stage
#
# Run a single hook on the whole tree:
# pre-commit run HOOK_ID --all-files
default_stages: [pre-commit]
repos:
- repo: local
hooks:
# ========================================================================
# PRE-COMMIT STAGE — formatters + cheap hygiene checks
# Everything here MUST be fast (< 1s per file). No linters.
# ========================================================================
# --- Nix ---
- id: nixpkgs-fmt
name: Format Nix files
entry: nixpkgs-fmt
language: system
files: '\.nix$'
description: Format Nix files using nixpkgs-fmt
# --- Shell ---
- id: shfmt
name: Format shell scripts
entry: shfmt
language: system
files: '\.sh$'
args: [-w, -i, "2", -bn, -ci]
description: Format shell scripts with shfmt
# --- Lua ---
- id: stylua
name: Format Lua files
entry: stylua
language: system
files: '\.lua$'
description: Format Lua files with stylua
# --- TOML ---
- id: taplo
name: Format TOML files
entry: taplo fmt
language: system
files: '\.toml$'
description: Format TOML files with taplo
# --- JSON ---
- id: json-format
name: Format JSON files
entry: bash -c 'for f in "$@"; do jq . "$f" > "$f.tmp" && mv "$f.tmp" "$f"; done'
language: system
files: '\.json$'
exclude: 'package-lock\.json|flake\.lock'
description: Format JSON files with jq
# --- Python ---
- id: ruff-format
name: Format Python files
entry: ruff format
language: system
files: '\.py$'
description: Format Python files with ruff
# --- GitHub Actions (scoped to workflow files only — fast even when it runs) ---
- id: actionlint
name: Lint GitHub Actions
entry: actionlint
language: system
files: '^\.github/workflows/.*\.(yml|yaml)$'
description: Lint GitHub Actions workflows
# --- Generic file hygiene ---
- id: trailing-whitespace
name: Trim trailing whitespace
entry: bash -c 'for f in "$@"; do sed -i "s/[[:space:]]*$//" "$f"; done'
language: system
files: '\.(nix|sh|lua|toml|yaml|yml|json|md|py)$'
description: Remove trailing whitespace
- id: end-of-file
name: Fix end of file
entry: "bash -c 'r=0; for f in \"$@\"; do if [ -s \"$f\" ] && [ -n \"$(tail -c1 \"$f\")\" ]; then echo >> \"$f\"; echo \"Fixed: $f\"; r=1; fi; done; exit $r' _"
language: system
files: '\.(nix|sh|lua|toml|yaml|yml|json|md|py)$'
description: Ensure files end with newline
- id: check-merge-conflict
name: Check for merge conflicts
# Match real git conflict markers exactly:
# <<<<<<< <ref> (7 chevrons + space)
# ======= (exactly 7 equals, alone on the line)
# >>>>>>> <ref> (7 chevrons + space)
# The previous regex matched any line starting with 7+ equals, which
# gave false positives on markdown setext-style headings (`====...`)
# and visual section dividers.
entry: bash -c 'for f in "$@"; do ! grep -qE "^(<{7} |={7}$|>{7} )" "$f" || { echo "Merge conflict in $f"; exit 1; }; done'
language: system
files: '\.(nix|sh|lua|toml|yaml|yml|json|md|py)$'
description: Check for merge conflict markers
- id: check-executables-have-shebangs
name: Check executables have shebangs
entry: bash -c 'for f in "$@"; do if [ -x "$f" ] && ! head -1 "$f" | grep -qE "^#!"; then echo "Missing shebang in $f"; exit 1; fi; done'
language: system
files: '\.sh$'
description: Ensure executables have shebangs
# --- Justfile validation (instant) ---
- id: just-validate
name: Validate justfile
entry: just --list
language: system
files: "justfile$"
pass_filenames: false
description: Validate justfile syntax
# ========================================================================
# PRE-PUSH STAGE — linters that may take a few seconds.
# Runs only on `git push`, not every commit. CI runs equivalents too,
# but these catch problems before they hit the PR.
# ========================================================================
- id: deadnix
name: Check for dead Nix code
entry: bash -c 'for f in "$@"; do deadnix --fail "$f" || exit 1; done' --
language: system
files: '\.nix$'
stages: [pre-push]
description: Check changed Nix files for dead/unused code
- id: shellcheck
name: Lint shell scripts
entry: shellcheck
language: system
files: '\.sh$'
# SC1091: external sourced files; SC2034: vars used externally;
# SC2086: word-splitting (often intentional in our scripts);
# SC2329/SC2317: false positives for functions called via case dispatch;
# SC2029: ssh client-side expansion is intentional in our admin scripts;
# SC2009: pgrep not always available in minimal live envs.
args: [-e, SC1091, -e, SC2034, -e, SC2086, -e, SC2329, -e, SC2317, -e, SC2029, -e, SC2009]
stages: [pre-push]
description: Lint shell scripts with shellcheck
- id: yamllint
name: Lint YAML files
entry: yamllint
language: system
files: '\.(yaml|yml)$'
args: [-d, relaxed]
stages: [pre-push]
description: Lint YAML files with yamllint
- id: markdownlint
name: Lint Markdown files
entry: markdownlint
language: system
files: '\.md$'
args: [--fix]
stages: [pre-push]
description: Lint and fix Markdown files
- id: ruff-check
name: Lint Python files
entry: ruff check
language: system
files: '\.py$'
args: [--fix]
stages: [pre-push]
description: Lint Python files with ruff
- id: typos
name: Check spelling
# --config required: typos auto-discovers `typos.toml` (no dot
# prefix), not `.typos.toml`. Without --config the project's
# extend-ignore-re rules silently don't apply.
entry: typos --config .typos.toml
language: system
pass_filenames: false
stages: [pre-push]
description: Check for typos in code and documentation
# ========================================================================
# DROPPED HOOKS — left here as comments so the rationale is discoverable.
#
# - nixpkgs-lint (was: nixpkgs-lint-community, Perl + tree-sitter)
# Pathologically slow on files with large embedded heredocs (e.g.
# modules/services/litellm-router.nix wedged for 7+ minutes on a
# single 145-line file in one observed run). CI's `lint-check` job
# already runs nixpkgs-fmt + deadnix + shellcheck across the whole
# tree, so we lose nothing actionable.
#
# - flake-check (was: `nix flake check --no-build` on every .nix change)
# Evaluates the entire flake across all 3 hosts + 141 modules on
# every commit — 30–120s wall time. CI runs `check-configurations`
# per host on every PR, which is the same thing. Locally,
# `just validate-quick` or `just test-host HOST` covers it on demand.
# ========================================================================