Files
cacti/tests/tools/pre-commit-check.sh

199 lines
5.2 KiB
Bash
Executable File

#!/bin/sh
# +-------------------------------------------------------------------------+
# | Pre-commit validation wrapper for Cacti |
# | Checks environment and dev tools before running linters. |
# +-------------------------------------------------------------------------+
set -eu
VENDOR_BIN="include/vendor/bin"
REQUIRED_PHP_MAJOR=8
MAX_PHP_MINOR=4
# ---- Environment checks (run before any tool) ----
check_php_version() {
php_ver=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null || echo "unknown")
major=$(echo "$php_ver" | cut -d. -f1)
minor=$(echo "$php_ver" | cut -d. -f2)
if [ "$major" != "$REQUIRED_PHP_MAJOR" ]; then
echo "WARNING: PHP $php_ver detected. Cacti requires PHP 8.x"
elif [ "$minor" -gt "$MAX_PHP_MINOR" ] 2>/dev/null; then
echo "WARNING: PHP $php_ver detected. Cacti targets PHP 8.1-8.4."
echo " Some type features (true|string) are not available on PHP 8.1."
echo " Consider: export PATH=\"/opt/homebrew/opt/php@8.4/bin:\${PATH}\""
fi
}
# Staged PHP files, one per line. Paths are read back with `read -r`, so
# spaces survive; quotePath=false keeps non-ASCII names unescaped.
staged_php_files() {
git -c core.quotePath=false diff --cached --name-only --diff-filter=ACMR -- '*.php'
}
check_merge_conflicts() {
staged=$(staged_php_files)
if [ -z "$staged" ]; then
return 0
fi
# Inspect the staged blob: a marker resolved only in the working tree is
# still committed.
conflicts=$(printf '%s\n' "$staged" | while IFS= read -r f; do
if git show ":$f" | grep -E -q '^(<<<<<<<|=======$|>>>>>>>)'; then
printf '%s\n' "$f"
fi
done)
if [ -n "$conflicts" ]; then
echo ""
echo "ERROR: Merge conflict markers found in staged files:"
echo "$conflicts"
echo " Resolve conflicts before committing."
echo ""
exit 1
fi
}
check_composer_lock() {
# Cacti tracks its lockfile so installs use reviewed dependency versions.
if git diff --cached --name-only | grep -Eq '^composer\.(json|lock)$'; then
composer validate --strict --no-check-publish
fi
}
check_vendor_dev_deps() {
php tests/tools/check_vendor_policy.php
}
check_autoload_freshness() {
if [ ! -f include/vendor/autoload.php ]; then
echo "WARNING: your are missing the composer autoload. Please run composer install"
exit 1
fi
}
check_tool() {
tool="$1"
label="$2"
if [ ! -x "$VENDOR_BIN/$tool" ]; then
echo ""
echo "ERROR: $label not found at $VENDOR_BIN/$tool"
echo ""
echo " Run: composer install --ignore-platform-reqs"
echo ""
echo " Dev dependencies must be installed for pre-commit hooks."
echo " This is a one-time setup after cloning or switching branches."
echo ""
exit 1
fi
# Verify the tool can actually load (catches missing Symfony/autoload issues)
if ! "$VENDOR_BIN/$tool" --version > /dev/null 2>&1; then
echo ""
echo "ERROR: $label exists but failed to load. Autoload may be stale."
echo ""
echo " Run: composer install --ignore-platform-reqs"
echo ""
exit 1
fi
}
# ---- Lint / analysis tools ----
# Lint the staged blobs with php -l. The previous phplint branch linted the
# working tree and its guard could never be true, so it never ran.
run_lint() {
echo "Running php -l on staged files..."
staged=$(staged_php_files)
if [ -z "$staged" ]; then
echo " No staged PHP files to check."
return 0
fi
failed=$(printf '%s\n' "$staged" | while IFS= read -r f; do
if ! git show ":$f" | php -l > /dev/null 2>&1; then
printf '%s\n' "$f"
fi
done)
if [ -n "$failed" ]; then
echo "Syntax errors in staged files:"
printf '%s\n' "$failed" | while IFS= read -r f; do
printf ' %s\n' "$f"
git show ":$f" | php -l 2>&1 || true
done
exit 1
fi
echo " All staged PHP files pass syntax check."
}
run_phpcsfixer() {
check_tool "php-cs-fixer" "PHP CS Fixer"
echo "Running PHP CS Fixer (dry-run)..."
if ! composer run-script php-cs-fixer; then
echo ""
echo "TIP: To auto-fix formatting issues, run:"
echo " composer run-script php-cs-fixit"
echo ""
exit 1
fi
}
run_phpstan() {
check_tool "phpstan" "PHPStan"
echo "Running PHPStan..."
composer run-script phpstan
}
# ---- Pre-flight checks (always run) ----
run_preflight() {
check_php_version
check_merge_conflicts
check_composer_lock
check_vendor_dev_deps
check_autoload_freshness
}
# ---- Main ----
case "${1:-all}" in
lint)
run_preflight
run_lint
;;
phpcsfixer)
run_preflight
run_phpcsfixer
;;
phpstan)
run_preflight
run_phpstan
;;
all)
run_preflight
run_lint
run_phpcsfixer
run_phpstan
;;
preflight)
run_preflight
echo "Pre-flight checks passed."
;;
*)
echo "Usage: $0 {lint|phpcsfixer|phpstan|all|preflight}"
exit 1
;;
esac