#!/usr/bin/env bash # +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2026 The Cacti Group | # | | # | This program is free software; you can redistribute it and/or | # | modify it under the terms of the GNU General Public License | # | as published by the Free Software Foundation; either version 2 | # | of the License, or (at your option) any later version. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDtool-based Graphing Solution | # +-------------------------------------------------------------------------+ # # Pre-commit hook: runs Cacti code-quality guard-rails on staged PHP files. # # Install once: # composer run-script install-hooks # # The hook skips the plugins/ directory (third-party code) and include/vendor/. # Only staged files trigger the checks; unstaged changes are not inspected. set -euo pipefail REPO_ROOT="$(git rev-parse --show-toplevel)" cd "$REPO_ROOT" php tests/tools/check_vendor_policy.php # Collect staged PHP files outside excluded directories. NUL-delimited names # keep paths with spaces, tabs, or glob characters intact. STAGED=() while IFS= read -r -d '' FILE; do case "$FILE" in plugins/*|include/vendor/*) ;; *.php) STAGED+=("$FILE") ;; esac done < <(git diff --cached --name-only -z --diff-filter=ACMR) if [ "${#STAGED[@]}" -eq 0 ]; then exit 0 fi echo "Running guard-rails on staged files..." # Check the staged blobs, not the working tree. Partial staging means an # unstaged fix must not hide a staged error, and an unstaged error must not # block a clean commit. STAGED_TREE="$(mktemp -d)" trap 'rm -rf "$STAGED_TREE"' EXIT printf '%s\0' "${STAGED[@]}" | git checkout-index -z --stdin --prefix="$STAGED_TREE/" FAIL=0 for FILE in "${STAGED[@]}"; do if ! php -l "$STAGED_TREE/$FILE" > /dev/null 2>&1; then echo "SYNTAX ERROR: $FILE" FAIL=1 fi done if [ "$FAIL" -ne 0 ]; then echo "Fix syntax errors before committing." exit 1 fi # PHP-CS-FIXER dry-run (style check, no auto-fix). # Runs only if php-cs-fixer is available via Composer. # --path-mode=override checks exactly the staged copies using the configured # rules, so no staged file is silently skipped due to finder exclusions. # Running inside the staged tree keeps reported paths repository-relative. if [ -x "include/vendor/bin/php-cs-fixer" ]; then if ! (cd "$STAGED_TREE" && "$REPO_ROOT/include/vendor/bin/php-cs-fixer" fix \ --allow-unsupported-php-version=yes \ --dry-run \ --diff \ --using-cache=no \ --config="$REPO_ROOT/.php-cs-fixer.php" \ --path-mode=override \ -- "${STAGED[@]}"); then echo "" echo "Style violations found. Run: composer run-script php-cs-fixit" exit 1 fi fi echo "Guard-rails passed."