forked from trent_larson/crowd-funder-for-time-pwa
Implements comprehensive pre-commit hook system to prevent debug code from reaching protected branches while maintaining developer choice. - Hooks stored in scripts/git-hooks/ (not in .git tree) - Deliberate installation required - no forced behavior - Automated installation script for team members - Comprehensive testing - Branch-aware execution (protected vs feature branches) - Configurable patterns and protected branch list Philosophy: Each developer chooses whether to use the hook, ensuring team flexibility while providing powerful debug code prevention tools.
214 lines
6.5 KiB
Bash
Executable File
214 lines
6.5 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# TimeSafari Pre-commit Hook - Debug Code Checker
|
|
# Only runs on master or specified branches to catch debug code before it reaches production
|
|
|
|
# Hook directory
|
|
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
CONFIG_FILE="$HOOK_DIR/debug-checker.config"
|
|
|
|
# Default configuration (fallback if config file is missing)
|
|
DEFAULT_PROTECTED_BRANCHES=("master" "main" "production" "release")
|
|
DEFAULT_DEBUG_PATTERNS=(
|
|
"console\."
|
|
"Debug:"
|
|
"debug:"
|
|
"DEBUG_"
|
|
"debug_"
|
|
"<!-- debug"
|
|
"debug.*="
|
|
)
|
|
|
|
# Load configuration from file if it exists
|
|
load_config() {
|
|
if [[ -f "$CONFIG_FILE" ]]; then
|
|
# Source the config file to load variables
|
|
# We'll use a safer approach by reading and parsing
|
|
PROTECTED_BRANCHES=()
|
|
DEBUG_PATTERNS=()
|
|
SKIP_PATTERNS=()
|
|
|
|
# Read protected branches
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^PROTECTED_BRANCHES=\( ]]; then
|
|
# Start reading array
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^\)$ ]]; then
|
|
break
|
|
fi
|
|
if [[ "$line" =~ \"([^\"]+)\" ]]; then
|
|
PROTECTED_BRANCHES+=("${BASH_REMATCH[1]}")
|
|
fi
|
|
done
|
|
fi
|
|
done < "$CONFIG_FILE"
|
|
|
|
# Read debug patterns
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^DEBUG_PATTERNS=\( ]]; then
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^\)$ ]]; then
|
|
break
|
|
fi
|
|
if [[ "$line" =~ \"([^\"]+)\" ]]; then
|
|
DEBUG_PATTERNS+=("${BASH_REMATCH[1]}")
|
|
fi
|
|
done
|
|
fi
|
|
done < "$CONFIG_FILE"
|
|
|
|
# Read skip patterns
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^SKIP_PATTERNS=\( ]]; then
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^\)$ ]]; then
|
|
break
|
|
fi
|
|
if [[ "$line" =~ \"([^\"]+)\" ]]; then
|
|
SKIP_PATTERNS+=("${BASH_REMATCH[1]}")
|
|
fi
|
|
done
|
|
fi
|
|
done < "$CONFIG_FILE"
|
|
fi
|
|
|
|
# Use defaults if config loading failed
|
|
if [[ ${#PROTECTED_BRANCHES[@]} -eq 0 ]]; then
|
|
PROTECTED_BRANCHES=("${DEFAULT_PROTECTED_BRANCHES[@]}")
|
|
fi
|
|
|
|
if [[ ${#DEBUG_PATTERNS[@]} -eq 0 ]]; then
|
|
DEBUG_PATTERNS=("${DEFAULT_DEBUG_PATTERNS[@]}")
|
|
fi
|
|
|
|
if [[ ${#SKIP_PATTERNS[@]} -eq 0 ]]; then
|
|
SKIP_PATTERNS=("${DEFAULT_SKIP_PATTERNS[@]}")
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
# Check if current branch is protected
|
|
is_protected_branch() {
|
|
local branch="$1"
|
|
for protected in "${PROTECTED_BRANCHES[@]}"; do
|
|
if [[ "$branch" == "$protected" ]]; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Check if file should be skipped
|
|
should_skip_file() {
|
|
local file="$1"
|
|
for pattern in "${SKIP_PATTERNS[@]}"; do
|
|
if [[ "$file" =~ $pattern ]]; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
# Load configuration
|
|
load_config
|
|
|
|
# Get current branch name
|
|
CURRENT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
|
|
|
|
if [[ -z "$CURRENT_BRANCH" ]]; then
|
|
echo "⚠️ Could not determine current branch, skipping debug check"
|
|
exit 0
|
|
fi
|
|
|
|
# Check if we should run the hook
|
|
if ! is_protected_branch "$CURRENT_BRANCH"; then
|
|
echo "🔒 Pre-commit hook skipped - not on protected branch ($CURRENT_BRANCH)"
|
|
echo " Protected branches: ${PROTECTED_BRANCHES[*]}"
|
|
exit 0
|
|
fi
|
|
|
|
echo "🔍 Running debug code check on protected branch: $CURRENT_BRANCH"
|
|
echo " Using config: $CONFIG_FILE"
|
|
|
|
# Get all staged files (modified, added, copied, merged)
|
|
ALL_STAGED_FILES=$(git diff --cached --name-only)
|
|
|
|
|
|
|
|
if [ -z "$ALL_STAGED_FILES" ]; then
|
|
echo "✅ No staged files to check"
|
|
exit 0
|
|
fi
|
|
|
|
# Initialize error tracking
|
|
ERRORS_FOUND=0
|
|
ERROR_MESSAGES=()
|
|
FILES_CHECKED=0
|
|
|
|
# Check each staged file for debug patterns
|
|
for file in $ALL_STAGED_FILES; do
|
|
# Skip files that should be ignored
|
|
if should_skip_file "$file"; then
|
|
continue
|
|
fi
|
|
|
|
FILES_CHECKED=$((FILES_CHECKED + 1))
|
|
|
|
# Check for debug patterns in the file
|
|
for pattern in "${DEBUG_PATTERNS[@]}"; do
|
|
# For new files, check the file content directly
|
|
# For modified files, check the staged diff
|
|
if [[ -f "$file" ]]; then
|
|
# New file - check content directly
|
|
if grep -E "$pattern" "$file" > /dev/null; then
|
|
ERRORS_FOUND=$((ERRORS_FOUND + 1))
|
|
ERROR_MESSAGES+=("🚨 $file: Found debug pattern '$pattern'")
|
|
fi
|
|
else
|
|
# Modified file - check staged diff
|
|
if git diff --cached "$file" | grep -E "$pattern" > /dev/null; then
|
|
ERRORS_FOUND=$((ERRORS_FOUND + 1))
|
|
ERROR_MESSAGES+=("🚨 $file: Found debug pattern '$pattern'")
|
|
fi
|
|
fi
|
|
done
|
|
done
|
|
|
|
# Report results
|
|
if [ $ERRORS_FOUND -gt 0 ]; then
|
|
echo ""
|
|
echo "❌ Debug code detected in staged files!"
|
|
echo " Branch: $CURRENT_BRANCH"
|
|
echo " Files checked: $FILES_CHECKED"
|
|
echo " Errors found: $ERRORS_FOUND"
|
|
echo ""
|
|
for msg in "${ERROR_MESSAGES[@]}"; do
|
|
echo " $msg"
|
|
done
|
|
echo ""
|
|
echo "💡 Please remove debug code before committing to $CURRENT_BRANCH"
|
|
echo " Common debug patterns to check:"
|
|
echo " - console.log, console.debug, console.error"
|
|
echo " - Debug: or debug: in templates"
|
|
echo " - DEBUG_ constants"
|
|
echo " - HTML comments with debug"
|
|
echo ""
|
|
echo " If debug code is intentional, consider:"
|
|
echo " - Moving to a feature branch first"
|
|
echo " - Using proper logging levels (logger.info, logger.debug)"
|
|
echo " - Adding debug code to .gitignore or .debugignore"
|
|
echo ""
|
|
echo " Configuration file: $CONFIG_FILE"
|
|
exit 1
|
|
else
|
|
echo "✅ No debug code found in $FILES_CHECKED staged files"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|