diff --git a/.claude/settings.json b/.claude/settings.json index b06b7c5..b113566 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -11,6 +11,7 @@ "Bash(git branch -r:*)", "Bash(git branch -v:*)", "Bash(git fetch:*)", + "Bash(git push:*)", "Bash(ls:*)", "Bash(tree:*)", "Bash(wc:*)", @@ -50,7 +51,6 @@ "mcp__ide__getDiagnostics" ], "deny": [ - "Bash(git push:*)", "Bash(git reset:*)", "Bash(git clean:*)", "Bash(git branch -D:*)", diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100755 index 0000000..47dfb8b --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# +# Block direct pushes to protected branches (main / master / release/*). +# Feature branches pass through untouched. Git feeds each ref being pushed on +# stdin as " ", so this checks +# the resolved destination and catches bare `git push` as well as explicit ones. +# GitHub branch protection is the server-side backstop; this is the local guard. +set -euo pipefail + +protected='^refs/heads/(main|master|release/.*)$' + +while read -r _local_ref _local_sha remote_ref _remote_sha; do + if [[ "$remote_ref" =~ $protected ]]; then + echo "pre-push: direct pushes to '${remote_ref#refs/heads/}' are blocked - open a PR instead." >&2 + exit 1 + fi +done + +exit 0 diff --git a/justfile b/justfile index d7fc96c..86a8489 100644 --- a/justfile +++ b/justfile @@ -5,10 +5,15 @@ default: # Create virtual environment and install dependencies venv: pip install uv - uv venv - source .venv/bin/activate + uv venv + source .venv/bin/activate + @just install-hooks @just install +# Install git hooks (points core.hooksPath at .githooks; idempotent, safe to re-run) +install-hooks: + git config core.hooksPath .githooks + # Install dependencies install: uv pip install -e ".[dev]"