#!/bin/bash # Fenrir Composite Pre-commit Hook # # This hook combines version management and code quality validation. # It first runs the version management logic, then runs validation. set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[1;34m' NC='\033[0m' # No Color echo -e "${BLUE}Fenrir Pre-commit Validation${NC}" echo "==================================" # Get the repository root REPO_ROOT=$(git rev-parse --show-toplevel) cd "$REPO_ROOT" # ============================================================================ # PART 1: VERSION MANAGEMENT (existing logic) # ============================================================================ echo -e "\n${YELLOW}1. Version Management...${NC}" # Check if SKIP_VERSION_UPDATE is set if [[ "${SKIP_VERSION_UPDATE}" = "1" ]]; then echo -e "${YELLOW}Notice: Skipping version update due to SKIP_VERSION_UPDATE=1${NC}" else # Verify .git/versionpath exists if [[ ! -f ".git/versionpath" ]]; then echo -e "${RED}Error: .git/versionpath not found. Please create it with contents:${NC}" echo -e "${YELLOW}versionFile=\"path/to/your/version/file\"${NC}" exit 1 fi # Source the version path file source ".git/versionpath" # Validate that versionFile variable was set if [[ -z "$versionFile" ]]; then echo -e "${RED}Error: versionFile variable not set in .git/versionpath${NC}" exit 1 fi # Get current date components year=$(date +%Y) month=$(date +%m) day=$(date +%d) # Create new version string newVersion="$year.$month.$day" # Get current branch name branchName=$(git rev-parse --abbrev-ref HEAD) # Check if we're in the middle of a merge if [[ -f ".git/MERGE_HEAD" ]]; then echo -e "${YELLOW}Warning: In the middle of a merge. Skipping version update.${NC}" else # Check if file exists relative to git root if [[ ! -f "$versionFile" ]]; then echo -e "${RED}Error: Version file not found at $versionFile${NC}" exit 1 fi # Store original version file content originalContent=$(cat "$versionFile") # Check if version actually needs updating if ! grep -q "version = \"$newVersion\"" "$versionFile"; then # Update the version in the file sed -i "s/version = [\"']\{0,1\}[0-9.]\+[\"']\{0,1\}/version = \"$newVersion\"/" "$versionFile" fi # Check if codeName exists and isn't "stable" if grep -q "codeName.*=.*\"stable\"" "$versionFile"; then # Don't modify stable codeName : elif grep -q "codeName.*=.*\"$branchName\"" "$versionFile"; then # CodeName already matches branch name, no need to update : elif grep -q "codeName" "$versionFile"; then # Update existing codeName sed -i "s/codeName = [\"']\{0,1\}[^\"']*[\"']\{0,1\}/codeName = \"$branchName\"/" "$versionFile" else # Add codeName after the version line sed -i "/version = / a\codeName = \"$branchName\"" "$versionFile" fi # Check if the file was actually modified if [[ "$(cat "$versionFile")" != "$originalContent" ]]; then echo -e "${GREEN}✓ Version file updated to $newVersion${NC}" if ! git diff --cached --quiet "$versionFile"; then echo -e "${YELLOW}Notice: Version file was already staged, updates made to staged version${NC}" else git add "$versionFile" echo -e "${YELLOW}Notice: Version file has been staged${NC}" fi else echo -e "${GREEN}✓ No version updates needed${NC}" fi fi fi # ============================================================================ # PART 2: CODE QUALITY VALIDATION (our new logic) # ============================================================================ echo -e "\n${YELLOW}2. Code Quality Validation...${NC}" # Track validation results VALIDATION_FAILED=0 # 2a. Python Syntax Validation echo -e "\n${YELLOW} 2a. Validating Python syntax...${NC}" if python3 tools/validate_syntax.py --check-only >/dev/null 2>&1; then echo -e "${GREEN} ✓ Syntax validation passed${NC}" else echo -e "${RED} ✗ Syntax validation failed${NC}" echo " Run: python3 tools/validate_syntax.py --fix" VALIDATION_FAILED=1 fi # 2a2. PEP8/flake8 Validation (for staged Python files only) echo -e "\n${YELLOW} 2a2. Checking PEP8 compliance...${NC}" if command -v flake8 >/dev/null 2>&1 && [ -n "$STAGED_PYTHON_FILES" ]; then PEP8_ISSUES=0 # Check staged Python files with flake8 # Focus on critical issues, ignore cosmetic ones for pre-commit FLAKE8_SELECT="E9,F63,F7,F82" # Critical syntax/import errors only FLAKE8_IGNORE="E501,W503,E203" # Ignore line length and some formatting for file in $STAGED_PYTHON_FILES; do if [ -f "$file" ]; then flake8_output=$(flake8 --select="$FLAKE8_SELECT" --ignore="$FLAKE8_IGNORE" "$file" 2>/dev/null || true) if [ -n "$flake8_output" ]; then if [ $PEP8_ISSUES -eq 0 ]; then echo -e "${RED} ✗ Critical PEP8 issues found:${NC}" fi echo -e "${RED} $file:${NC}" echo "$flake8_output" | sed 's/^/ /' PEP8_ISSUES=1 fi fi done if [ $PEP8_ISSUES -eq 0 ]; then echo -e "${GREEN} ✓ No critical PEP8 issues in staged files${NC}" else echo -e "${RED} ✗ Critical PEP8 issues found${NC}" echo -e "${YELLOW} Run: flake8 --select=E9,F63,F7,F82 for details${NC}" VALIDATION_FAILED=1 fi elif [ -n "$STAGED_PYTHON_FILES" ]; then echo -e "${YELLOW} ⚠ flake8 not available (install with: pip install flake8)${NC}" else echo -e "${GREEN} ✓ No Python files to check${NC}" fi # 2b. Check for common issues in modified files echo -e "\n${YELLOW} 2b. Checking modified files for common issues...${NC}" # Get list of staged files (all types) STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM || true) STAGED_PYTHON_FILES=$(echo "$STAGED_FILES" | grep '\.py$' || true) if [ -n "$STAGED_FILES" ]; then ISSUES_FOUND=0 # Check for cache files being committed CACHE_FILES=$(echo "$STAGED_FILES" | grep -E '(__pycache__|\.pyc$)' || true) if [ -n "$CACHE_FILES" ]; then echo -e "${RED} ✗ Python cache files staged for commit:${NC}" echo "$CACHE_FILES" | while read cache_file; do echo -e "${RED} $cache_file${NC}" done echo -e "${RED} ✗ Run: python3 tools/cleanup_cache.py --remove${NC}" ISSUES_FOUND=1 fi # Check Python files for specific issues if [ -n "$STAGED_PYTHON_FILES" ]; then for file in $STAGED_PYTHON_FILES; do if [ -f "$file" ]; then # Check for unterminated strings (the main issue from the email) if grep -n 'f".*{$' "$file" >/dev/null 2>&1; then echo -e "${RED} ✗ $file: Potential unterminated f-string${NC}" ISSUES_FOUND=1 fi # Check for missing imports that are commonly used if grep -q 'debug\.DebugLevel\.' "$file" && ! grep -q 'from.*debug' "$file" && ! grep -q 'import.*debug' "$file"; then echo -e "${YELLOW} ⚠ $file: Uses debug.DebugLevel but no debug import found${NC}" fi fi done fi if [ $ISSUES_FOUND -eq 0 ]; then echo -e "${GREEN} ✓ No common issues found in staged files${NC}" else echo -e "${RED} ✗ Common issues found in staged files${NC}" VALIDATION_FAILED=1 fi else echo -e "${GREEN} ✓ No files staged for commit${NC}" fi # 2c. Quick import test for core modules (informational only) echo -e "\n${YELLOW} 2c. Testing core module imports...${NC}" IMPORT_WARNINGS=0 # Test core imports that are critical (but don't fail on import issues - might be dependency related) CORE_MODULES=( "src.fenrirscreenreader.core.fenrirManager" "src.fenrirscreenreader.core.commandManager" "src.fenrirscreenreader.core.eventManager" ) cd src for module in "${CORE_MODULES[@]}"; do if python3 -c "import $module" 2>/dev/null; then echo -e "${GREEN} ✓ $module${NC}" else echo -e "${YELLOW} ⚠ $module (import failed - might be dependency related)${NC}" IMPORT_WARNINGS=1 fi done cd "$REPO_ROOT" if [ $IMPORT_WARNINGS -eq 1 ]; then echo -e "${YELLOW} ⚠ Some core module imports failed (non-blocking)${NC}" echo -e "${YELLOW} This may be due to missing runtime dependencies${NC}" else echo -e "${GREEN} ✓ Core module imports successful${NC}" fi # ============================================================================ # FINAL SUMMARY # ============================================================================ echo -e "\n============================================================" if [ $VALIDATION_FAILED -eq 0 ]; then echo -e "${GREEN}✓ All pre-commit validations passed${NC}" echo -e "${GREEN}✓ Version management completed${NC}" echo -e "${GREEN}✓ Code quality checks passed${NC}" echo -e "${GREEN}Commit allowed to proceed${NC}" # Show skip option echo -e "\n${BLUE}Tip: You can skip version updates with SKIP_VERSION_UPDATE=1${NC}" exit 0 else echo -e "${RED}✗ Pre-commit validation failed${NC}" echo -e "${RED}Commit blocked - please fix issues above${NC}" echo "" echo "Quick fixes:" echo " • Python syntax: python3 tools/validate_syntax.py --fix" echo " • Review flagged files manually" echo " • Re-run commit after fixes" echo "" echo -e "${BLUE}Note: Version management completed successfully${NC}" exit 1 fi