#!/bin/bash # Quality Validation Script for Enhanced Workflow # Enforces TDD, low-LOC, UTC timestamps, and code quality rules 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 MAX_LOC=300 MAX_LOC_JUSTIFIED=350 PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TASK_ID="" FINAL_CHECK=false # Help function show_help() { echo "Usage: $0 [--final]" echo "" echo "Validates code quality before task completion:" echo " - TDD compliance (tests written before code)" echo " - LOC rules (files under 300 LOC, 350 max if justified)" echo " - UTC timestamp compliance" echo " - Code formatting and linting" echo "" echo "Options:" echo " --final Perform final validation before task completion" echo " --help Show this help message" echo "" echo "Examples:" echo " $0 15 # Validate task 15" echo " $0 15 --final # Final validation for task 15" } # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --final) FINAL_CHECK=true shift ;; --help) show_help exit 0 ;; -*) echo -e "${RED}Error: Unknown option $1${NC}" show_help exit 1 ;; *) if [[ -z "$TASK_ID" ]]; then TASK_ID="$1" else echo -e "${RED}Error: Multiple task IDs specified${NC}" exit 1 fi shift ;; esac done # Validate task ID if [[ -z "$TASK_ID" ]]; then echo -e "${RED}Error: Task ID is required${NC}" show_help exit 1 fi echo -e "${BLUE}๐Ÿ” Quality Validation for Task $TASK_ID${NC}" if [[ "$FINAL_CHECK" == "true" ]]; then echo -e "${YELLOW}โš ๏ธ Performing FINAL validation - task completion blocked if any checks fail${NC}" fi echo "" # Function to check if a file is Python code is_python_file() { local file="$1" [[ "$file" == *.py ]] } # Function to count lines of code (excluding comments and empty lines) count_loc() { local file="$1" if [[ ! -f "$file" ]]; then echo 0 return fi # Count non-comment, non-empty lines grep -v '^\s*#' "$file" | grep -v '^\s*$' | wc -l } # Function to check UTC timestamp compliance check_utc_timestamps() { local file="$1" local issues=() if [[ ! -f "$file" ]]; then return 0 fi # Check for naive datetime usage if grep -q "datetime\.now()" "$file"; then issues+=("naive datetime.now() - should use datetime.now(timezone.utc)") fi # Check for deprecated utcnow if grep -q "datetime\.utcnow()" "$file"; then issues+=("deprecated datetime.utcnow() - should use datetime.now(timezone.utc)") fi # Check for time.time() usage in timing contexts if grep -q "time\.time()" "$file"; then issues+=("time.time() usage - consider using datetime for consistency") fi # Check for inconsistent filename formats if grep -q "strftime.*%Y.*%m.*%d.*%H.*%M.*%S" "$file"; then if ! grep -q "strftime.*%Y%m%d_%H%M%S" "$file"; then issues+=("inconsistent filename timestamp format - should use YYYYMMDD_HHMMSS") fi fi if [[ ${#issues[@]} -gt 0 ]]; then echo -e "${RED}โŒ UTC Timestamp Issues in $file:${NC}" for issue in "${issues[@]}"; do echo -e " ${RED}โ€ข $issue${NC}" done return 1 fi return 0 } # Function to check LOC compliance check_loc_compliance() { local file="$1" local loc if [[ ! -f "$file" ]]; then return 0 fi loc=$(count_loc "$file") if [[ $loc -gt $MAX_LOC_JUSTIFIED ]]; then echo -e "${RED}โŒ LOC Violation in $file: $loc lines (exceeds $MAX_LOC_JUSTIFIED limit)${NC}" return 1 elif [[ $loc -gt $MAX_LOC ]]; then echo -e "${YELLOW}โš ๏ธ LOC Warning in $file: $loc lines (exceeds $MAX_LOC, but under $MAX_LOC_JUSTIFIED)${NC}" echo -e " ${YELLOW} Consider splitting this file for better maintainability${NC}" return 0 # Warning, not error else echo -e "${GREEN}โœ… LOC OK: $file ($loc lines)${NC}" return 0 fi } # Function to check test coverage check_test_coverage() { local task_id="$1" local test_files=() local source_files=() # Find test files related to this task while IFS= read -r -d '' file; do if [[ "$file" == *"test"* ]] && [[ "$file" == *.py ]]; then test_files+=("$file") fi done < <(find "$PROJECT_ROOT/tests" -name "*.py" -print0) # Find source files that might need testing while IFS= read -r -d '' file; do if [[ "$file" == *.py ]] && [[ "$file" != *"__init__.py" ]]; then source_files+=("$file") fi done < <(find "$PROJECT_ROOT/src" -name "*.py" -print0) echo -e "${BLUE}๐Ÿ“Š Test Coverage Analysis:${NC}" if [[ ${#test_files[@]} -eq 0 ]]; then echo -e "${YELLOW}โš ๏ธ No test files found in tests/ directory${NC}" return 1 fi echo -e "${GREEN}โœ… Found ${#test_files[@]} test files${NC}" # Check if tests are passing if command -v pytest >/dev/null 2>&1; then echo -e "${BLUE}๐Ÿงช Running tests...${NC}" if cd "$PROJECT_ROOT" && python -m pytest --tb=short -q; then echo -e "${GREEN}โœ… All tests passing${NC}" else echo -e "${RED}โŒ Some tests are failing${NC}" if [[ "$FINAL_CHECK" == "true" ]]; then return 1 fi fi else echo -e "${YELLOW}โš ๏ธ pytest not available - skipping test execution${NC}" fi return 0 } # Function to check code formatting check_code_formatting() { echo -e "${BLUE}๐ŸŽจ Checking code formatting...${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}" else echo -e "${RED}โŒ Code formatting issues detected${NC}" echo -e "${YELLOW} Run: uv run black src/ tests/ to fix${NC}" if [[ "$FINAL_CHECK" == "true" ]]; then return 1 fi fi else echo -e "${YELLOW}โš ๏ธ black not available - skipping formatting check${NC}" fi 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}" else echo -e "${RED}โŒ Linting issues detected${NC}" echo -e "${YELLOW} Run: uv run ruff check --fix src/ tests/ to fix${NC}" if [[ "$FINAL_CHECK" == "true" ]]; then return 1 fi fi else echo -e "${YELLOW}โš ๏ธ ruff not available - skipping linting check${NC}" fi return 0 } # Main validation function main() { local exit_code=0 local validation_results=() echo -e "${BLUE}๐Ÿš€ Starting Quality Validation...${NC}" echo "" # Check LOC compliance for all Python files echo -e "${BLUE}๐Ÿ“ Checking LOC compliance...${NC}" while IFS= read -r -d '' file; do if is_python_file "$file"; then if ! check_loc_compliance "$file"; then validation_results+=("LOC_VIOLATION:$file") if [[ "$FINAL_CHECK" == "true" ]]; then exit_code=1 fi fi fi done < <(find "$PROJECT_ROOT/src" -name "*.py" -print0) echo "" # Check UTC timestamp compliance echo -e "${BLUE}โฐ Checking UTC timestamp compliance...${NC}" while IFS= read -r -d '' file; do if is_python_file "$file"; then if ! check_utc_timestamps "$file"; then validation_results+=("UTC_VIOLATION:$file") if [[ "$FINAL_CHECK" == "true" ]]; then exit_code=1 fi fi fi done < <(find "$PROJECT_ROOT/src" -name "*.py" -print0) echo "" # Check test coverage if ! check_test_coverage "$TASK_ID"; then validation_results+=("TEST_VIOLATION") if [[ "$FINAL_CHECK" == "true" ]]; then exit_code=1 fi fi echo "" # Check code formatting if ! check_code_formatting; then validation_results+=("FORMAT_VIOLATION") if [[ "$FINAL_CHECK" == "true" ]]; then exit_code=1 fi fi echo "" # Summary echo -e "${BLUE}๐Ÿ“‹ Validation Summary:${NC}" if [[ ${#validation_results[@]} -eq 0 ]]; then echo -e "${GREEN}๐ŸŽ‰ All quality checks passed!${NC}" echo -e "${GREEN}โœ… Task $TASK_ID is ready for completion${NC}" else echo -e "${RED}โŒ Quality issues found:${NC}" for result in "${validation_results[@]}"; do case "$result" in LOC_VIOLATION:*) echo -e " ${RED}โ€ข LOC violation in ${result#LOC_VIOLATION:}${NC}" ;; UTC_VIOLATION:*) echo -e " ${RED}โ€ข UTC timestamp violation in ${result#UTC_VIOLATION:}${NC}" ;; TEST_VIOLATION) echo -e " ${RED}โ€ข Test coverage or execution issues${NC}" ;; FORMAT_VIOLATION) echo -e " ${RED}โ€ข Code formatting or linting issues${NC}" ;; esac done if [[ "$FINAL_CHECK" == "true" ]]; then echo "" echo -e "${RED}๐Ÿšซ Task completion BLOCKED due to quality violations${NC}" echo -e "${YELLOW} Fix all issues before marking task as complete${NC}" else echo "" echo -e "${YELLOW}โš ๏ธ Quality issues detected but not blocking progress${NC}" echo -e "${YELLOW} Fix issues before final validation${NC}" fi 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}โ€ข Run tests:${NC} uv run pytest" echo -e " ${BLUE}โ€ข Re-validate:${NC} $0 $TASK_ID" exit $exit_code } # Run main function main "$@"