githooks: pre-push runs quality gate checks on committed files not the working tree

This commit is contained in:
djmil 2026-06-16 18:55:57 +00:00
parent bda2e22b35
commit 59a68133a3

View File

@ -1,37 +1,57 @@
#!/usr/bin/env bash
# pre-push hook: runs quality checks before every `git push`.
# pre-push hook: quality gate for the *pushed* commits — a poor man's CI.
# Install with: make setup (sets core.hooksPath = .githooks)
#
# Why a throwaway worktree: a gate must judge what you publish, not the state of
# your desk. Reading the live working tree lets uncommitted WIP either break the
# gate or sneak past it. So we check out the exact commit being pushed into a
# clean, isolated worktree and run the checks there.
set -euo pipefail
REPO_ROOT=$(git rev-parse --show-toplevel)
# shellcheck source=../tools.versions
source "${REPO_ROOT}/tools.versions"
echo "pre-push: running checks..."
run_checks() { # cwd is a clean checkout of the pushed commit
echo " → gofmt"
local unformatted
unformatted=$(gofmt -l $(git ls-files '*.go'))
if [ -n "$unformatted" ]; then
echo " FAIL: the following files are not gofmt-formatted:"
echo "$unformatted" | sed 's/^/ /'
echo " Fix with: make lint-fix"
return 1
fi
# ── 1. gofmt ──────────────────────────────────────────────────────────────────
echo " → gofmt"
UNFORMATTED=$(gofmt -l $(git ls-files '*.go'))
if [ -n "$UNFORMATTED" ]; then
echo " FAIL: the following files are not gofmt-formatted:"
echo "$UNFORMATTED" | sed 's/^/ /'
echo " Fix with: make lint-fix"
exit 1
fi
echo " → go vet"
go vet ./...
# ── 2. go vet ─────────────────────────────────────────────────────────────────
echo " → go vet"
go vet ./...
echo " → golangci-lint"
go run github.com/golangci/golangci-lint/cmd/golangci-lint@"${GOLANGCI_LINT_VERSION}" run ./...
# ── 3. golangci-lint ──────────────────────────────────────────────────────────
echo " → golangci-lint"
go run github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION} run ./...
echo " → gosec"
go run github.com/securego/gosec/v2/cmd/gosec@"${GOSEC_VERSION}" -quiet ./...
# ── 4. gosec (security scan) ──────────────────────────────────────────────────
echo " → gosec"
go run github.com/securego/gosec/v2/cmd/gosec@${GOSEC_VERSION} -quiet ./...
# govulncheck is intentionally omitted (network + slow).
# Run it manually with: make security
}
# govulncheck is intentionally omitted (network + slow).
# Run it manually with: make security
# pre-push feeds one "<local_ref> <local_sha> <remote_ref> <remote_sha>" line
# per pushed ref on stdin.
while read -r _local_ref local_sha _remote_ref _remote_sha; do
# Branch deletion (all-zero sha): nothing to check.
[[ "$local_sha" =~ ^0+$ ]] && continue
echo "pre-push: checking ${local_sha:0:12} in a clean worktree..."
tmp=$(mktemp -d)
git worktree add --detach --quiet "$tmp" "$local_sha"
if ( cd "$tmp" && run_checks ); then
git worktree remove --force "$tmp"
else
git worktree remove --force "$tmp"
echo "pre-push: checks FAILED for ${local_sha:0:12}"
exit 1
fi
done
echo "pre-push: all checks passed."