feat(git): add pre-push hook to reject unsigned commits #148

Merged
rootiest merged 2 commits from chore/pre-push-signature-check into main 2026-09-13 00:58:16 +00:00
Owner

Summary

  • Adds a tracked .githooks/pre-push hook that scans every commit about to be pushed on a branch ref and refuses the push if any of them is unsigned (%G? = N) or has a bad signature (%G? = B)
  • Root cause: Gittyup (the GUI git client) commits via libgit2 directly and never invokes gpg, silently ignoring commit.gpgsign — this is how an unsigned commit reached main in the first place
  • Bypassable per-push with git push --no-verify for legitimate edge cases
  • Chains to any global/system core.hooksPath hook of the same name, matching the existing pattern in scripts/agents-tools/hooks/pre-commit
  • Wiring core.hooksPath to point at .githooks/ is intentionally not included here — that's a per-machine/contributor concern, not something the majority of fish-config users need, so it lives in user-dots (~/.config/.user-dots/fish/local.fish) instead

Test plan

  • Manually verified: pushing a branch containing an unsigned commit is rejected with a clear error message and exit code 1
  • Manually verified: pushing a branch containing a signed commit succeeds
  • python3 docs/verify-manual.py — 76/76 passed (no doc regressions)
  • fish -n syntax check on touched fish files
## Summary - Adds a tracked `.githooks/pre-push` hook that scans every commit about to be pushed on a branch ref and refuses the push if any of them is unsigned (`%G?` = `N`) or has a bad signature (`%G?` = `B`) - Root cause: Gittyup (the GUI git client) commits via libgit2 directly and never invokes `gpg`, silently ignoring `commit.gpgsign` — this is how an unsigned commit reached `main` in the first place - Bypassable per-push with `git push --no-verify` for legitimate edge cases - Chains to any global/system `core.hooksPath` hook of the same name, matching the existing pattern in `scripts/agents-tools/hooks/pre-commit` - Wiring `core.hooksPath` to point at `.githooks/` is intentionally **not** included here — that's a per-machine/contributor concern, not something the majority of fish-config users need, so it lives in user-dots (`~/.config/.user-dots/fish/local.fish`) instead ## Test plan - [x] Manually verified: pushing a branch containing an unsigned commit is rejected with a clear error message and exit code 1 - [x] Manually verified: pushing a branch containing a signed commit succeeds - [x] `python3 docs/verify-manual.py` — 76/76 passed (no doc regressions) - [x] `fish -n` syntax check on touched fish files
rootiest added 1 commit 2026-09-13 00:52:12 +00:00
Gittyup commits via libgit2 directly and never invokes gpg, silently
ignoring commit.gpgsign — root cause of an unsigned commit reaching
main. Adds a tracked .githooks/pre-push that rejects any push
carrying a commit with no signature or a bad signature, bypassable
with --no-verify. Wiring core.hooksPath to it is a per-machine
concern, done separately in user-dots, not shipped here.
rootiest added the Kind/SecurityArea/Scripts labels 2026-09-13 00:52:18 +00:00
rootiest added 1 commit 2026-09-13 00:56:55 +00:00
Contributors need to know .githooks/pre-push exists and how to point
their clone at it, since it isn't wired up automatically.
rootiest added the Area/Docs label 2026-09-13 00:57:10 +00:00
rootiest merged commit 0736de384a into main 2026-09-13 00:58:16 +00:00
rootiest deleted branch chore/pre-push-signature-check 2026-09-13 00:58:16 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rootiest/fish-config#148