268 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			268 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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 code_name exists and isn't "stable"
 | |
|         if grep -q "code_name.*=.*\"stable\"" "$versionFile"; then
 | |
|             # Don't modify stable code_name
 | |
|             :
 | |
|         elif grep -q "code_name.*=.*\"$branchName\"" "$versionFile"; then
 | |
|             # code_name already matches branch name, no need to update
 | |
|             :
 | |
|         elif grep -q "code_name" "$versionFile"; then
 | |
|             # Update existing code_name
 | |
|             sed -i "s/code_name = [\"']\{0,1\}[^\"']*[\"']\{0,1\}/code_name = \"$branchName\"/" "$versionFile"
 | |
|         else
 | |
|             # Add code_name after the version line
 | |
|             sed -i "/version = / a\code_name = \"$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 <file> 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 |