forked from jsnbuchanan/crowd-funder-for-time-pwa
feat(git): implement debug code prevention system with deliberate installation
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.
This commit is contained in:
213
scripts/git-hooks/pre-commit
Executable file
213
scripts/git-hooks/pre-commit
Executable file
@@ -0,0 +1,213 @@
|
||||
#!/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 "$@"
|
||||
Reference in New Issue
Block a user