-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpre-commit
More file actions
executable file
·79 lines (72 loc) · 4.16 KB
/
Copy pathpre-commit
File metadata and controls
executable file
·79 lines (72 loc) · 4.16 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
#!/usr/bin/env bash
# Local name guard — run on every clone via the global ~/.githooks dispatcher
# (which chains the repo's committed .githooks/<name>).
#
# Refuses to commit any name listed in an UNTRACKED banlist. The banlist lives
# only on this machine (.abcd/.work.local/ is gitignored), so a name that must
# never enter tracked or public content — e.g. a private repo/project name — is
# enforced here WITHOUT the name ever appearing in a committed file or in public
# CI config (which a repo may be, or become). No-op when the banlist is absent
# (fresh clones, CI), so it never blocks a machine that has not opted in.
#
# One pattern (extended-regex, case-insensitive) per line; blank lines and lines
# beginning with '#' are ignored. Companion to the public docs-lint harness-name
# gate: that one bans *public* harness names in CI; this one bans *private* names
# locally, keeping their literal string out of every published artifact.
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
# --- iss-62 identity gate ---------------------------------------------------
# Refuse a commit whose author identity diverges from the committed pin
# (.abcd/config/identity.json). Self-contained (no binary dependency) so the
# guard holds even before abcd is built or on PATH, and can't be broken by a
# stale abcd. Runs FIRST — the name-guard below early-exits when no banlist is
# present, which must not skip this check. No-op ONLY when the repo has no pin.
#
# FAIL CLOSED: a pin that is present but that this shell cannot read (empty
# value, or non-lowercase / non-canonical JSON keys the sed does not match)
# BLOCKS the commit rather than falling through. The Go check
# (`abcd ahoy identity-check`) enforces such pins directly; this shell guard is
# deliberately stricter — it never permits an unverified identity. Keep the two
# in the same fail-closed direction (see identity_test.go / the shell hook test).
identity_pin=".abcd/config/identity.json"
if [ -f "$identity_pin" ]; then
want_name=$(sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$identity_pin" | head -n1)
want_email=$(sed -n 's/.*"email"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$identity_pin" | head -n1)
if [ -z "$want_name" ] || [ -z "$want_email" ]; then
echo "pre-commit: BLOCKED — $identity_pin is present but its name/email could not be read (fail closed)." >&2
echo " check the pin's JSON (lowercase \"name\"/\"email\", both non-empty), or run: abcd ahoy identity-check" >&2
exit 1
fi
have_name=$(git config user.name || true)
have_email=$(git config user.email || true)
if [ "$have_name" != "$want_name" ] || [ "$have_email" != "$want_email" ]; then
echo "pre-commit: BLOCKED — git identity \"$have_name <$have_email>\" does not match the pinned \"$want_name <$want_email>\" ($identity_pin)." >&2
echo " fix: git config user.name \"$want_name\" && git config user.email \"$want_email\"" >&2
exit 1
fi
fi
# --- end iss-62 identity gate -----------------------------------------------
banlist=".abcd/.work.local/private-names.txt"
# Refresh the generated block from the user-level sources corpus when present
# (itd-76 dogfood): keeps the banlist as fresh as the current commit. No-op on
# machines without the corpus; a failed refresh falls back to the existing
# (possibly stale) banlist with a warning rather than blocking the commit.
sync_banlist="$HOME/.abcd/sources/bin/sync-banlist"
if [ -x "$sync_banlist" ]; then
"$sync_banlist" "$(pwd)" >/dev/null 2>&1 \
|| echo "pre-commit: warning — banlist refresh failed; checking existing banlist." >&2
fi
[ -f "$banlist" ] || exit 0
# Only added lines of staged content matter (skip the +++ file headers).
added="$(git diff --cached --unified=0 --diff-filter=ACM | grep -E '^\+' | grep -vE '^\+\+\+' || true)"
[ -n "$added" ] || exit 0
rc=0
while IFS= read -r pat; do
case "$pat" in '' | \#*) continue ;; esac
if printf '%s\n' "$added" | grep -iqE -- "$pat"; then
echo "pre-commit: blocked — staged content matches a banned name (pattern in $banlist)." >&2
echo " reword to a generic term before committing; the banlist is untracked and local." >&2
rc=1
fi
done < "$banlist"
exit $rc