trax/scripts/quality_check.sh

149 lines
4.3 KiB
Bash
Executable File

#!/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