diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e98c9c..bc1a0ea 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,6 +48,18 @@ repos: hooks: - id: shellcheck + # Laravel Pint (PHP code style) + # CRITICAL: Uses --dirty to auto-fix only changed files (fast) + # Preflight script will verify with --test flag for CI parity + - repo: local + hooks: + - id: pint + name: Laravel Pint + entry: bash -c 'if [ -x vendor/bin/pint ]; then vendor/bin/pint --dirty; fi' + language: system + types: [php] + pass_filenames: false + # Git hooks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fdfad4..1d3155d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Pint `--test --dirty` workflow in preflight script for CI parity +- Pre-commit hook for Laravel Pint auto-formatting +- CHANGELOG validation in preflight script - Initial Laravel 12 setup with PostgreSQL support - PEST testing framework integration - PHPStan static analysis with Larastan diff --git a/scripts/preflight.sh b/scripts/preflight.sh index ac8d816..01b7e28 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -46,10 +46,18 @@ fi # Helper functions to reduce code duplication run_pint() { local cmd_prefix="$1" - # Run Laravel Pint to auto-fix code style issues - # Uses --dirty flag to only process modified files for performance + # Run Laravel Pint with check-first workflow (see SELF_REVIEW_CHECKLIST.md) + # CRITICAL: Always check before fixing to ensure CI parity + # --test: check-only mode (no auto-fix, matches CI behavior) + # --dirty: only process modified files (fast, focused) if [ -x ./vendor/bin/pint ]; then - ${cmd_prefix} ./vendor/bin/pint --dirty + echo "→ Checking code style (pint --test --dirty)..." + if ! ${cmd_prefix} ./vendor/bin/pint --test --dirty; then + echo "→ Auto-fixing code style issues (pint --dirty)..." + ${cmd_prefix} ./vendor/bin/pint --dirty + echo "→ Verifying fix matches CI requirements (pint --test --dirty)..." + ${cmd_prefix} ./vendor/bin/pint --test --dirty + fi fi } @@ -161,7 +169,45 @@ if [ -f docs/openapi.yaml ] && command -v npx >/dev/null 2>&1; then npx --yes @stoplight/spectral-cli lint docs/openapi.yaml fi -# 4) Check PR size locally (against BASE) +# 4) CHANGELOG validation (for non-docs branches) +# Branch prefixes that are exempt from CHANGELOG updates (configuration) +CHANGELOG_EXEMPT_PREFIXES="^(docs|chore|ci|test)/" +# Minimum lines in [Unreleased] to consider it non-empty +# Typically: 3 lines = one line each for Added, Changed, Fixed sections +MIN_CHANGELOG_LINES=3 + +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "") +if [ -f CHANGELOG.md ] && [ "$CURRENT_BRANCH" != "main" ] && [[ ! "$CURRENT_BRANCH" =~ $CHANGELOG_EXEMPT_PREFIXES ]]; then + # Check if CHANGELOG has [Unreleased] section + if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then + echo "❌ CHANGELOG.md missing [Unreleased] section" >&2 + echo "Tip: Every feature/fix/refactor branch must update CHANGELOG.md" >&2 + echo "Exempt branches: docs/*, chore/*, ci/*, test/*" >&2 + exit 1 + fi + + # Check if there's actual content after [Unreleased] (robust to last/only section) + # Find line number of [Unreleased], then extract content up to next heading or EOF + UNRELEASED_START=$(grep -n '^## \[Unreleased\]' CHANGELOG.md | cut -d: -f1) + if [ -n "$UNRELEASED_START" ]; then + # Find next heading after [Unreleased], or use EOF if none found + UNRELEASED_END=$(tail -n +"$((UNRELEASED_START + 1))" CHANGELOG.md | grep -n '^## ' | head -1 | cut -d: -f1) + if [ -n "$UNRELEASED_END" ]; then + # Extract content between [Unreleased] and next heading + UNRELEASED_CONTENT=$(sed -n "$((UNRELEASED_START + 1)),$((UNRELEASED_START + UNRELEASED_END - 1))p" CHANGELOG.md | grep -v '^##' | grep -v '^$' | grep -v '^' | wc -l) + else + # [Unreleased] is the last section, extract all remaining content + UNRELEASED_CONTENT=$(tail -n +"$((UNRELEASED_START + 1))" CHANGELOG.md | grep -v '^##' | grep -v '^$' | grep -v '^' | wc -l) + fi + + if [ "$UNRELEASED_CONTENT" -lt "$MIN_CHANGELOG_LINES" ]; then + echo "⚠️ Warning: [Unreleased] section appears empty in CHANGELOG.md" >&2 + echo "Did you forget to document your changes?" >&2 + fi + fi +fi + +# 5) Check PR size locally (against BASE) if ! git rev-parse -q --verify "origin/$BASE" >/dev/null 2>&1; then echo "Warning: Cannot verify base branch origin/$BASE - skipping PR size check." >&2 echo "Tip: Run 'git fetch origin $BASE' to enable PR size checking." >&2