hooks: actually replace the generator in setup-hooks.sh (ebabdce shipped the old one)
ebabdce's message said setup-hooks.sh now activates the tracked hook instead of
generating a copy of it. The committed file was still the generator. This is that
change, for real.
How the wrong file got committed, since the mechanism is worth knowing: I wrote the new
setup-hooks.sh, ran it, then hit an unrelated bad test and undid it with `git reset
--hard HEAD~1`. setup-hooks.sh is TRACKED, so my uncommitted edit to it was reverted by
that reset -- while the new, UNTRACKED hooks/pre-commit survived, because reset --hard
does not touch untracked files. `git add setup-hooks.sh` then staged the restored
generator, and the commit described what I had written rather than what was in the tree.
Nothing failed loudly: the file was valid shell, shellcheck passed, and I verified the
HOOK's behaviour rather than the SCRIPT's, so the evidence I collected was real but
about the wrong file.
Effect of the defect: hooks/pre-commit and core.hooksPath were correct, so this clone
was gated the whole time. But anyone running ./setup-hooks.sh would have re-created
.git/hooks/pre-commit -- reinstating the very copy ebabdce removed, where it is now
INERT (core.hooksPath wins) and looks like protection while never running.
The activator now also VERIFIES its own effect: it reads core.hooksPath back and exits
1 if it is not 'hooks', because a gate that was never activated behaves exactly like one
with nothing to report. Checked before committing this time: 0 heredoc generators
present, core.hooksPath set at line 20, and running it creates NO .git/hooks/pre-commit.
This commit is contained in:
+31
-31
@@ -1,39 +1,39 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Install git hooks for this project
|
# Activate the repo's tracked git hooks for this clone.
|
||||||
|
#
|
||||||
|
# Hooks live under hooks/ (version-controlled) and are wired via core.hooksPath,
|
||||||
|
# so every machine gets the same hooks after running this once.
|
||||||
|
#
|
||||||
|
# This script used to GENERATE .git/hooks/pre-commit from a heredoc. That made the
|
||||||
|
# running hook a copy, and a copy drifts: measured 2026-09-19, the installed hook on
|
||||||
|
# one machine was an older revision than this script emitted, having lost the Linux
|
||||||
|
# gitleaks install hint. core.hooksPath runs the tracked file itself, so the hook
|
||||||
|
# that runs and the hook in git history cannot disagree.
|
||||||
|
#
|
||||||
|
# core.hooksPath is LOCAL config and is never cloned, which is why this step exists
|
||||||
|
# at all: a fresh clone has hooks/ and no active gate until someone runs this.
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
HOOK_DIR="$(git rev-parse --show-toplevel)/.git/hooks"
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
mkdir -p "$HOOK_DIR"
|
|
||||||
|
|
||||||
# --- pre-commit hook: secret scanning with gitleaks ---
|
git config core.hooksPath hooks
|
||||||
cat > "$HOOK_DIR/pre-commit" << 'HOOK'
|
chmod +x hooks/* 2>/dev/null || true
|
||||||
#!/bin/bash
|
|
||||||
# Pre-commit hook — scans staged files for secrets using gitleaks
|
|
||||||
|
|
||||||
if ! command -v gitleaks >/dev/null 2>&1; then
|
# Prove the setting took rather than trusting that it did: a hook that was never
|
||||||
echo ""
|
# activated behaves exactly like one that has nothing to report.
|
||||||
echo "⚠️ gitleaks is not installed — skipping secret scan"
|
active="$(git config --get core.hooksPath || true)"
|
||||||
echo " Install: brew install gitleaks (macOS)"
|
if [ "$active" != "hooks" ]; then
|
||||||
echo " Or: curl -sSL https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_\$(uname -s)_\$(uname -m).tar.gz | sudo tar -xz -C /usr/local/bin gitleaks"
|
echo "❌ core.hooksPath is '$active', not 'hooks' — the gate is NOT active." >&2
|
||||||
echo ""
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🔒 Scanning for secrets..."
|
|
||||||
|
|
||||||
if gitleaks protect --staged --no-banner 2>/dev/null; then
|
|
||||||
echo "✅ No secrets detected"
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
echo "❌ Secrets detected in staged changes — commit blocked"
|
|
||||||
echo ""
|
|
||||||
echo " Details: gitleaks protect --staged --verbose"
|
|
||||||
echo " Bypass: git commit --no-verify"
|
|
||||||
echo ""
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
HOOK
|
|
||||||
|
|
||||||
chmod +x "$HOOK_DIR/pre-commit"
|
# A hook left behind by the old copy-based install would be shadowed by
|
||||||
echo "✅ Pre-commit hook installed (.git/hooks/pre-commit)"
|
# core.hooksPath and never run again, so say so rather than leaving a decoy.
|
||||||
|
if [ -e .git/hooks/pre-commit ]; then
|
||||||
|
echo "⚠️ .git/hooks/pre-commit still exists and is now INERT (core.hooksPath wins)."
|
||||||
|
echo " It is a leftover from the old copy-based install; remove it:"
|
||||||
|
echo " rm .git/hooks/pre-commit"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ core.hooksPath set to hooks/ — tracked hooks are now active"
|
||||||
|
echo " (pre-commit secret scan via gitleaks)"
|
||||||
|
|||||||
Reference in New Issue
Block a user