githooks: pre-push runs quality gate checks on committed files not the working tree
This commit is contained in:
parent
bda2e22b35
commit
59a68133a3
@ -1,37 +1,57 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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)
|
# 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
|
set -euo pipefail
|
||||||
|
|
||||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||||
# shellcheck source=../tools.versions
|
# shellcheck source=../tools.versions
|
||||||
source "${REPO_ROOT}/tools.versions"
|
source "${REPO_ROOT}/tools.versions"
|
||||||
|
|
||||||
echo "pre-push: running checks..."
|
run_checks() { # cwd is a clean checkout of the pushed commit
|
||||||
|
|
||||||
# ── 1. gofmt ──────────────────────────────────────────────────────────────────
|
|
||||||
echo " → gofmt"
|
echo " → gofmt"
|
||||||
UNFORMATTED=$(gofmt -l $(git ls-files '*.go'))
|
local unformatted
|
||||||
if [ -n "$UNFORMATTED" ]; then
|
unformatted=$(gofmt -l $(git ls-files '*.go'))
|
||||||
|
if [ -n "$unformatted" ]; then
|
||||||
echo " FAIL: the following files are not gofmt-formatted:"
|
echo " FAIL: the following files are not gofmt-formatted:"
|
||||||
echo "$UNFORMATTED" | sed 's/^/ /'
|
echo "$unformatted" | sed 's/^/ /'
|
||||||
echo " Fix with: make lint-fix"
|
echo " Fix with: make lint-fix"
|
||||||
exit 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 2. go vet ─────────────────────────────────────────────────────────────────
|
|
||||||
echo " → go vet"
|
echo " → go vet"
|
||||||
go vet ./...
|
go vet ./...
|
||||||
|
|
||||||
# ── 3. golangci-lint ──────────────────────────────────────────────────────────
|
|
||||||
echo " → golangci-lint"
|
echo " → golangci-lint"
|
||||||
go run github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION} run ./...
|
go run github.com/golangci/golangci-lint/cmd/golangci-lint@"${GOLANGCI_LINT_VERSION}" run ./...
|
||||||
|
|
||||||
# ── 4. gosec (security scan) ──────────────────────────────────────────────────
|
|
||||||
echo " → gosec"
|
echo " → gosec"
|
||||||
go run github.com/securego/gosec/v2/cmd/gosec@${GOSEC_VERSION} -quiet ./...
|
go run github.com/securego/gosec/v2/cmd/gosec@"${GOSEC_VERSION}" -quiet ./...
|
||||||
|
|
||||||
# govulncheck is intentionally omitted (network + slow).
|
# govulncheck is intentionally omitted (network + slow).
|
||||||
# Run it manually with: make security
|
# 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."
|
echo "pre-push: all checks passed."
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user