trax/scripts/validate_formatting.sh

217 lines
7.3 KiB
Bash
Executable File

#!/bin/bash
# Formatting Validation Script
# Checks code formatting and linting compliance
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# Function to check if a file is Python code
is_python_file() {
local file="$1"
[[ "$file" == *.py ]]
}
# Function to check code formatting with black
check_black_formatting() {
echo -e "${BLUE}🎨 Checking code formatting with black...${NC}"
if command -v black >/dev/null 2>&1; then
if cd "$PROJECT_ROOT" && python -m black --check --diff src/ tests/ 2>/dev/null; then
echo -e " ${GREEN}✅ Code formatting OK${NC}"
return 0
else
echo -e " ${RED}❌ Code formatting issues detected${NC}"
echo -e " ${YELLOW} Run: uv run black src/ tests/ to fix${NC}"
return 1
fi
else
echo -e " ${YELLOW}⚠️ black not available - skipping formatting check${NC}"
echo -e " ${YELLOW} Install with: uv pip install black${NC}"
return 0 # Not a failure, just unavailable
fi
}
# Function to check code linting with ruff
check_ruff_linting() {
echo -e "${BLUE}🔍 Checking code linting with ruff...${NC}"
if command -v ruff >/dev/null 2>&1; then
if cd "$PROJECT_ROOT" && python -m ruff check src/ tests/ 2>/dev/null; then
echo -e " ${GREEN}✅ Linting OK${NC}"
return 0
else
echo -e " ${RED}❌ Linting issues detected${NC}"
echo -e " ${YELLOW} Run: uv run ruff check --fix src/ tests/ to fix${NC}"
return 1
fi
else
echo -e " ${YELLOW}⚠️ ruff not available - skipping linting check${NC}"
echo -e " ${YELLOW} Install with: uv pip install ruff${NC}"
return 0 # Not a failure, just unavailable
fi
}
# Function to check import organization
check_import_organization() {
echo -e "${BLUE}📦 Checking import organization...${NC}"
local import_issues=0
while IFS= read -r -d '' file; do
if is_python_file "$file"; then
# Check for multiple import statements that could be combined
local import_lines=$(grep -c "^import\|^from " "$file" 2>/dev/null || echo 0)
local from_lines=$(grep -c "^from " "$file" 2>/dev/null || echo 0)
if [[ $import_lines -gt 5 ]]; then
echo -e " ${YELLOW}⚠️ Many import statements in $file ($import_lines) - consider organizing${NC}"
import_issues=$((import_issues + 1))
fi
# Check for unused imports (basic check)
if [[ $from_lines -gt 0 ]]; then
local unused_imports=0
while IFS= read -r import_line; do
local module=$(echo "$import_line" | sed 's/^from \([^ ]*\) import.*/\1/')
if [[ -n "$module" ]] && ! grep -q "$module\." "$file" 2>/dev/null; then
if [[ $unused_imports -eq 0 ]]; then
echo -e " ${YELLOW}⚠️ Potential unused imports in $file${NC}"
fi
unused_imports=$((unused_imports + 1))
import_issues=$((import_issues + 1))
fi
done < <(grep "^from " "$file" 2>/dev/null)
fi
fi
done < <(find "$PROJECT_ROOT/src" -name "*.py" -print0)
if [[ $import_issues -eq 0 ]]; then
echo -e " ${GREEN}✅ Import organization looks good${NC}"
return 0
else
echo -e " ${YELLOW}⚠️ $import_issues import organization issues found${NC}"
return 0 # Warning, not error
fi
}
# Function to check line length compliance
check_line_length() {
echo -e "${BLUE}📏 Checking line length compliance...${NC}"
local long_lines=0
local max_length=100 # Black default
while IFS= read -r -d '' file; do
if is_python_file "$file"; then
local file_long_lines=$(awk "length(\$0) > $max_length" "$file" 2>/dev/null | wc -l)
if [[ $file_long_lines -gt 0 ]]; then
echo -e " ${YELLOW}⚠️ $file_long_lines long lines in $file (max: $max_length chars)${NC}"
long_lines=$((long_lines + file_long_lines))
fi
fi
done < <(find "$PROJECT_ROOT/src" -name "*.py" -print0)
if [[ $long_lines -eq 0 ]]; then
echo -e " ${GREEN}✅ All lines within length limits${NC}"
return 0
else
echo -e " ${YELLOW}⚠️ $long_lines long lines found - consider breaking them up${NC}"
return 0 # Warning, not error
fi
}
# Function to provide formatting improvement recommendations
suggest_formatting_improvements() {
echo -e "${BLUE}💡 Formatting Improvement Recommendations:${NC}"
echo -e " ${YELLOW}• Use black for consistent code formatting${NC}"
echo -e " ${YELLOW}• Use ruff for fast Python linting${NC}"
echo -e " ${YELLOW}• Organize imports: standard library, third-party, local${NC}"
echo -e " ${YELLOW}• Keep lines under 100 characters${NC}"
echo -e " ${YELLOW}• Use consistent indentation (4 spaces)${NC}"
echo -e " ${YELLOW}• Remove trailing whitespace${NC}"
echo -e " ${YELLOW}• Use meaningful variable and function names${NC}"
}
# Main function
main() {
local exit_code=0
local formatting_issues=0
local linting_issues=0
echo -e "${BLUE}🎨 Formatting Validation for Project${NC}"
echo -e "${BLUE}Focus: Code formatting, linting, and style compliance${NC}"
echo ""
# Check black formatting
if ! check_black_formatting; then
formatting_issues=$((formatting_issues + 1))
exit_code=1
fi
echo ""
# Check ruff linting
if ! check_ruff_linting; then
linting_issues=$((linting_issues + 1))
exit_code=1
fi
echo ""
# Check import organization
check_import_organization
echo ""
# Check line length
check_line_length
echo ""
# Provide improvement recommendations
suggest_formatting_improvements
echo ""
# Summary
echo -e "${BLUE}📋 Formatting Validation Summary:${NC}"
if [[ $formatting_issues -eq 0 ]]; then
echo -e " ${GREEN}✅ Code formatting: PASSED${NC}"
else
echo -e " ${RED}❌ Code formatting: FAILED${NC}"
fi
if [[ $linting_issues -eq 0 ]]; then
echo -e " ${GREEN}✅ Code linting: PASSED${NC}"
else
echo -e " ${RED}❌ Code linting: FAILED${NC}"
fi
if [[ $exit_code -eq 0 ]]; then
echo ""
echo -e "${GREEN}🎉 All formatting checks passed!${NC}"
echo -e "${GREEN}✅ Code meets formatting and style standards${NC}"
else
echo ""
echo -e "${RED}🚫 Formatting validation failed - must be resolved before task completion${NC}"
echo -e "${YELLOW} Fix all formatting issues before proceeding${NC}"
fi
echo ""
echo -e "${BLUE}🔧 Quick Fix Commands:${NC}"
echo -e " ${BLUE}• Format code:${NC} uv run black src/ tests/"
echo -e " ${BLUE}• Fix linting:${NC} uv run ruff check --fix src/ tests/"
echo -e " ${BLUE}• Re-validate:${NC} $0"
exit $exit_code
}
# Run main function
main "$@"