#!/usr/bin/env bash # Pre-commit hook: mirrors GitHub CI checks locally. # Installed via: git config core.hooksPath .githooks # # Runs on every `git commit`. Catches format/lint/test failures before push. set -euo pipefail REPO_ROOT="$(git rev-parse --show-toplevel)" cd "$REPO_ROOT" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' _ok() { echo -e "${GREEN}✓${NC} $1"; } _fail() { echo -e "${RED}✗${NC} $1"; } _info() { echo -e "${YELLOW}→${NC} $1"; } echo "" _info "Running pre-commit checks (mirrors GitHub CI)..." echo "" # Only check staged shell/Go files to keep commits fast. STAGED=$(git diff --cached --name-only --diff-filter=ACM) HAS_SHELL=$(echo "$STAGED" | grep -E '\.sh$|^mole$|^bin/' || true) HAS_GO=$(echo "$STAGED" | grep -E '\.go$' || true) FAILED=0 # --- 1. Shell syntax check (fast, no tool required) --- if [[ -n "$HAS_SHELL" ]]; then _info "Shell syntax check..." while IFS= read -r f; do [[ -f "$f" ]] || continue if ! bash -n "$f" 2>&1; then _fail "Syntax error: $f" FAILED=1 fi done <<< "$HAS_SHELL" [[ $FAILED -eq 0 ]] && _ok "Shell syntax clean" fi # --- 2. shfmt format check (if installed) --- if [[ -n "$HAS_SHELL" ]] && command -v shfmt > /dev/null 2>&1; then _info "shfmt format check..." UNFORMATTED="" while IFS= read -r f; do [[ -f "$f" ]] || continue if ! shfmt -i 4 -ci -sr -d "$f" > /dev/null 2>&1; then UNFORMATTED="$UNFORMATTED $f" fi done <<< "$HAS_SHELL" if [[ -n "$UNFORMATTED" ]]; then _fail "shfmt: unformatted files:$UNFORMATTED" _info "Fix with: ./scripts/check.sh --format" FAILED=1 else _ok "shfmt format clean" fi fi # --- 3. shellcheck (if installed) --- if [[ -n "$HAS_SHELL" ]] && command -v shellcheck > /dev/null 2>&1; then _info "shellcheck..." while IFS= read -r f; do [[ -f "$f" ]] || continue if ! shellcheck "$f" 2>&1; then FAILED=1 fi done <<< "$HAS_SHELL" [[ $FAILED -eq 0 ]] && _ok "shellcheck clean" fi # --- 4. Go vet (if staged Go files) --- if [[ -n "$HAS_GO" ]] && command -v go > /dev/null 2>&1; then _info "go vet..." if go vet ./cmd/... 2>&1; then _ok "go vet clean" else _fail "go vet failed" FAILED=1 fi fi echo "" if [[ $FAILED -ne 0 ]]; then _fail "Pre-commit checks failed. Fix the issues above before committing." _info "Run './scripts/check.sh --format' to auto-fix formatting." echo "" exit 1 fi _ok "All pre-commit checks passed." echo ""