#!/bin/bash # # Pre-commit hook: ruff lint check on staged Python files. # Catches syntax errors, unused imports, and basic issues before commit. # To bypass in emergency: git commit --no-verify # RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' REPO_ROOT=$(git rev-parse --show-toplevel) cd "$REPO_ROOT" STAGED_PY=$(git diff --cached --name-only --diff-filter=ACM -z -- '*.py') if [ -z "$STAGED_PY" ]; then exit 0 fi echo "ruff check on staged files..." # Stash unstaged changes so ruff only operates on staged content. # Without this, ruff --fix runs on the full working tree file (staged + # unstaged), and the subsequent git add would silently include unstaged # changes in the commit — breaking git add -p workflows. STASHED=0 if git stash --keep-index -q 2>/dev/null; then STASHED=1 fi # Auto-fix what we can, then re-stage the fixed files printf '%s' "$STAGED_PY" | xargs -0 ruff check --fix --exit-zero printf '%s' "$STAGED_PY" | xargs -0 git add # Restore unstaged changes if [ $STASHED -eq 1 ]; then git stash pop -q fi # Now check for remaining unfixable issues printf '%s' "$STAGED_PY" | xargs -0 ruff check RUFF_EXIT=$? if [ $RUFF_EXIT -ne 0 ]; then echo "" echo -e "${RED}Pre-commit checks failed (unfixable issues). Commit blocked.${NC}" echo -e "${YELLOW}To bypass (not recommended): git commit --no-verify${NC}" exit 1 fi echo -e "${GREEN}All checks passed.${NC}" exit 0