#!/bin/bash # Code quality checker for Trax project set -e # Color codes GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' echo -e "${BLUE}๐Ÿ” Trax Code Quality Check${NC}" echo "================================" cd "$(dirname "$0")/.." # Activate virtual environment if [ -f ".venv/bin/activate" ]; then source .venv/bin/activate else echo -e "${RED}โŒ Virtual environment not found${NC}" exit 1 fi FAILED=0 # Black formatting check echo -e "\n${CYAN}๐Ÿ“ Black (formatting):${NC}" if uv run black --check src/ tests/ 2>/dev/null; then echo -e " ${GREEN}โœ… Code is properly formatted${NC}" else echo -e " ${YELLOW}โš ๏ธ Code needs formatting${NC}" echo -e " ${YELLOW}๐Ÿ’ก Run: uv run black src/ tests/${NC}" ((FAILED++)) fi # Ruff linting echo -e "\n${CYAN}๐Ÿ” Ruff (linting):${NC}" if uv run ruff check src/ tests/ 2>/dev/null; then echo -e " ${GREEN}โœ… No linting issues${NC}" else echo -e " ${YELLOW}โš ๏ธ Linting issues found${NC}" echo -e " ${YELLOW}๐Ÿ’ก Run: uv run ruff check --fix src/ tests/${NC}" ((FAILED++)) fi # MyPy type checking echo -e "\n${CYAN}๐Ÿ”ค MyPy (type checking):${NC}" if uv run mypy src/ 2>/dev/null; then echo -e " ${GREEN}โœ… Type checks pass${NC}" else echo -e " ${YELLOW}โš ๏ธ Type checking failed${NC}" echo -e " ${YELLOW}๐Ÿ’ก Fix type hints in reported files${NC}" ((FAILED++)) fi # File size check (300 LOC limit) echo -e "\n${CYAN}๐Ÿ“ File Size Check (300 LOC limit):${NC}" LARGE_FILES=0 for file in $(find src tests -name "*.py" -type f 2>/dev/null); do lines=$(wc -l < "$file") if [ "$lines" -gt 300 ]; then echo -e " ${YELLOW}โš ๏ธ $file: $lines lines${NC}" ((LARGE_FILES++)) fi done if [ $LARGE_FILES -eq 0 ]; then echo -e " ${GREEN}โœ… All files under 300 LOC${NC}" else echo -e " ${YELLOW}Found $LARGE_FILES files over 300 LOC${NC}" echo -e " ${YELLOW}๐Ÿ’ก Consider splitting large files${NC}" fi # Check for missing docstrings echo -e "\n${CYAN}๐Ÿ“š Docstring Check:${NC}" python3 << 'EOF' import ast import os from pathlib import Path missing_docstrings = [] def check_docstrings(filepath): with open(filepath, 'r') as f: try: tree = ast.parse(f.read()) except: return [] missing = [] for node in ast.walk(tree): if isinstance(node, (ast.FunctionDef, ast.ClassDef)): if not ast.get_docstring(node): missing.append(f"{filepath}:{node.lineno} - {node.name}") return missing # Check all Python files for root, dirs, files in os.walk('src'): for file in files: if file.endswith('.py') and not file.startswith('__'): filepath = os.path.join(root, file) missing = check_docstrings(filepath) missing_docstrings.extend(missing) if missing_docstrings: print(f" โš ๏ธ Found {len(missing_docstrings)} missing docstrings") for item in missing_docstrings[:5]: print(f" - {item}") if len(missing_docstrings) > 5: print(f" ... and {len(missing_docstrings) - 5} more") else: print(" โœ… All functions/classes have docstrings") EOF # Check imports echo -e "\n${CYAN}๐Ÿ“ฆ Import Check:${NC}" if uv run ruff check --select I src/ tests/ 2>/dev/null; then echo -e " ${GREEN}โœ… Imports are properly sorted${NC}" else echo -e " ${YELLOW}โš ๏ธ Import issues found${NC}" echo -e " ${YELLOW}๐Ÿ’ก Run: uv run ruff check --select I --fix src/ tests/${NC}" fi # Test discovery echo -e "\n${CYAN}๐Ÿงช Test Discovery:${NC}" test_count=$(find tests -name "test_*.py" -o -name "*_test.py" 2>/dev/null | wc -l | tr -d ' ') if [ "$test_count" -gt 0 ]; then echo -e " ${GREEN}โœ… Found $test_count test files${NC}" else echo -e " ${YELLOW}โš ๏ธ No test files found${NC}" echo -e " ${YELLOW}๐Ÿ’ก Add tests in tests/ directory${NC}" fi # Summary echo -e "\n${BLUE}Summary:${NC}" echo "================================" if [ $FAILED -eq 0 ] && [ $LARGE_FILES -eq 0 ]; then echo -e "${GREEN}โœ… All quality checks passed!${NC}" exit 0 else echo -e "${YELLOW}โš ๏ธ Some quality issues found${NC}" echo "" echo "Quick fix command:" echo -e "${CYAN}uv run black src/ tests/ && uv run ruff check --fix src/ tests/${NC}" exit 1 fi