| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- #!/bin/bash
- # hooks/pre-commit-lint.sh
- # PreToolUse hook - runs linter on staged files before commit
- # Matcher: Bash (when command contains "git commit")
- #
- # Configuration in .claude/settings.json:
- # {
- # "hooks": {
- # "PreToolUse": [{
- # "matcher": "Bash",
- # "hooks": ["bash hooks/pre-commit-lint.sh $TOOL_INPUT"]
- # }]
- # }
- # }
- INPUT="$1"
- # Only trigger on git commit commands
- if ! echo "$INPUT" | grep -qE 'git\s+commit'; then
- exit 0
- fi
- # Collect staged files
- STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null)
- if [[ -z "$STAGED_FILES" ]]; then
- exit 0
- fi
- ERRORS=0
- lint_js() {
- local files
- files=$(echo "$STAGED_FILES" | grep -E '\.(ts|tsx|js|jsx|mjs|cjs)$')
- if [[ -n "$files" ]]; then
- if command -v npx &>/dev/null && [[ -f "node_modules/.bin/eslint" ]]; then
- echo "Linting JS/TS files..."
- echo "$files" | xargs npx eslint --max-warnings 0 2>/dev/null
- return $?
- elif command -v biome &>/dev/null; then
- echo "Linting JS/TS files with Biome..."
- echo "$files" | xargs biome check 2>/dev/null
- return $?
- fi
- fi
- return 0
- }
- lint_python() {
- local files
- files=$(echo "$STAGED_FILES" | grep -E '\.py$')
- if [[ -n "$files" ]]; then
- if command -v ruff &>/dev/null; then
- echo "Linting Python files..."
- echo "$files" | xargs ruff check 2>/dev/null
- return $?
- elif command -v flake8 &>/dev/null; then
- echo "$files" | xargs flake8 2>/dev/null
- return $?
- fi
- fi
- return 0
- }
- lint_go() {
- local files
- files=$(echo "$STAGED_FILES" | grep -E '\.go$')
- if [[ -n "$files" ]]; then
- if command -v golangci-lint &>/dev/null; then
- echo "Linting Go files..."
- golangci-lint run --new-from-rev=HEAD 2>/dev/null
- return $?
- elif command -v go &>/dev/null; then
- go vet ./... 2>/dev/null
- return $?
- fi
- fi
- return 0
- }
- lint_rust() {
- local files
- files=$(echo "$STAGED_FILES" | grep -E '\.rs$')
- if [[ -n "$files" ]]; then
- if command -v cargo &>/dev/null && [[ -f "Cargo.toml" ]]; then
- echo "Linting Rust files..."
- cargo clippy --all-targets -- -D warnings 2>/dev/null
- return $?
- fi
- fi
- return 0
- }
- lint_php() {
- local files
- files=$(echo "$STAGED_FILES" | grep -E '\.php$')
- if [[ -n "$files" ]]; then
- if command -v ./vendor/bin/pint &>/dev/null; then
- echo "Linting PHP files..."
- echo "$files" | xargs ./vendor/bin/pint --test 2>/dev/null
- return $?
- elif command -v php &>/dev/null; then
- for f in $files; do
- php -l "$f" 2>/dev/null || return 1
- done
- fi
- fi
- return 0
- }
- # Run all applicable linters
- lint_js || ERRORS=$((ERRORS + 1))
- lint_python || ERRORS=$((ERRORS + 1))
- lint_go || ERRORS=$((ERRORS + 1))
- lint_rust || ERRORS=$((ERRORS + 1))
- lint_php || ERRORS=$((ERRORS + 1))
- if [[ $ERRORS -gt 0 ]]; then
- echo ""
- echo "LINT FAILED: $ERRORS linter(s) reported issues."
- echo "Fix the issues above before committing."
- exit 1
- fi
- exit 0
|