Files
everything-claude-code/scripts/codex-git-hooks/pre-push
ohashi-mizuki 0c166e14da fix: skip pre-push checks on branch deletion
The pre-push hook runs lint/typecheck/test/build checks on every push,
including `git push origin --delete <branch>`. Branch deletion does not
push any code, so verification checks are unnecessary and block the
delete operation.

Detect deletion pushes by reading stdin (local sha is all zeros for
deletes) and exit early.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:09:26 +09:00

125 lines
3.2 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# ECC Codex Git Hook: pre-push
# Runs a lightweight verification flow before pushes.
if [[ "${ECC_SKIP_GIT_HOOKS:-0}" == "1" || "${ECC_SKIP_PREPUSH:-0}" == "1" ]]; then
exit 0
fi
if [[ -f ".ecc-hooks-disable" || -f ".git/ecc-hooks-disable" ]]; then
exit 0
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
exit 0
fi
# Skip checks for branch deletion pushes (e.g., git push origin --delete <branch>).
# The pre-push hook receives lines on stdin: <local ref> <local sha> <remote ref> <remote sha>.
# For deletions, the local sha is the zero OID.
is_delete_only=true
while read -r _local_ref local_sha _remote_ref _remote_sha; do
if [[ "$local_sha" != "0000000000000000000000000000000000000000" ]]; then
is_delete_only=false
break
fi
done
if [[ "$is_delete_only" == "true" ]]; then
exit 0
fi
ran_any_check=0
log() {
printf '[ECC pre-push] %s\n' "$*"
}
fail() {
printf '[ECC pre-push] FAILED: %s\n' "$*" >&2
exit 1
}
detect_pm() {
if [[ -f "pnpm-lock.yaml" ]]; then
echo "pnpm"
elif [[ -f "bun.lockb" ]]; then
echo "bun"
elif [[ -f "yarn.lock" ]]; then
echo "yarn"
elif [[ -f "package-lock.json" ]]; then
echo "npm"
else
echo "npm"
fi
}
has_node_script() {
local script_name="$1"
node -e 'const fs=require("fs"); const p=JSON.parse(fs.readFileSync("package.json","utf8")); process.exit(p.scripts && p.scripts[process.argv[1]] ? 0 : 1)' "$script_name" >/dev/null 2>&1
}
run_node_script() {
local pm="$1"
local script_name="$2"
case "$pm" in
pnpm) pnpm run "$script_name" ;;
bun) bun run "$script_name" ;;
yarn) yarn "$script_name" ;;
npm) npm run "$script_name" ;;
*) npm run "$script_name" ;;
esac
}
if [[ -f "package.json" ]]; then
pm="$(detect_pm)"
log "Node project detected (package manager: $pm)"
for script_name in lint typecheck test build; do
if has_node_script "$script_name"; then
ran_any_check=1
log "Running: $script_name"
run_node_script "$pm" "$script_name" || fail "$script_name failed"
else
log "Skipping missing script: $script_name"
fi
done
if [[ "${ECC_PREPUSH_AUDIT:-0}" == "1" ]]; then
ran_any_check=1
log "Running dependency audit (ECC_PREPUSH_AUDIT=1)"
case "$pm" in
pnpm) pnpm audit --prod || fail "pnpm audit failed" ;;
bun) bun audit || fail "bun audit failed" ;;
yarn) yarn npm audit --recursive || fail "yarn audit failed" ;;
npm) npm audit --omit=dev || fail "npm audit failed" ;;
*) npm audit --omit=dev || fail "npm audit failed" ;;
esac
fi
fi
if [[ -f "go.mod" ]] && command -v go >/dev/null 2>&1; then
ran_any_check=1
log "Go project detected. Running: go test ./..."
go test ./... || fail "go test failed"
fi
if [[ -f "pyproject.toml" || -f "requirements.txt" ]]; then
if command -v pytest >/dev/null 2>&1; then
ran_any_check=1
log "Python project detected. Running: pytest -q"
pytest -q || fail "pytest failed"
else
log "Python project detected but pytest is not installed. Skipping."
fi
fi
if [[ "$ran_any_check" -eq 0 ]]; then
log "No supported checks found in this repository. Skipping."
else
log "Verification checks passed."
fi
exit 0