Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 50 additions & 4 deletions scripts/preflight.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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 '^<!--' | 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 '^<!--' | 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
Expand Down