feat: add unified code quality dashboard with multiple validators
- Add validator_type field to scans and violations (architecture, security, performance) - Create security validator with SEC-xxx rules - Create performance validator with PERF-xxx rules - Add base validator class for shared functionality - Add validate_all.py script to run all validators - Update code quality service with validator type filtering - Add validator type tabs to dashboard UI - Add validator type filter to violations list - Update stats response with per-validator breakdown - Add security and performance rules documentation - Add chat-bubble icons to icon library 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
218
scripts/validate_all.py
Executable file
218
scripts/validate_all.py
Executable file
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Unified Code Validator
|
||||
======================
|
||||
Runs all validation scripts (architecture, security, performance) in sequence.
|
||||
|
||||
This provides a single entry point for comprehensive code validation,
|
||||
useful for CI/CD pipelines and pre-commit hooks.
|
||||
|
||||
Usage:
|
||||
python scripts/validate_all.py # Run all validators
|
||||
python scripts/validate_all.py --security # Run only security validator
|
||||
python scripts/validate_all.py --performance # Run only performance validator
|
||||
python scripts/validate_all.py --architecture # Run only architecture validator
|
||||
python scripts/validate_all.py -v # Verbose output
|
||||
python scripts/validate_all.py --fail-fast # Stop on first failure
|
||||
python scripts/validate_all.py --json # JSON output
|
||||
|
||||
Options:
|
||||
--architecture Run architecture validator
|
||||
--security Run security validator
|
||||
--performance Run performance validator
|
||||
--fail-fast Stop on first validator failure
|
||||
-v, --verbose Show detailed output
|
||||
--errors-only Only show errors
|
||||
--json Output results as JSON
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from base_validator import Severity
|
||||
|
||||
|
||||
def run_architecture_validator(verbose: bool = False) -> tuple[int, dict]:
|
||||
"""Run the architecture validator"""
|
||||
try:
|
||||
# Import dynamically to avoid circular imports
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
from scripts.validate_architecture import ArchitectureValidator
|
||||
|
||||
config_path = Path.cwd() / ".architecture-rules.yaml"
|
||||
validator = ArchitectureValidator(config_path=config_path, verbose=verbose)
|
||||
result = validator.validate_all()
|
||||
|
||||
return (
|
||||
1 if result.has_errors() else 0,
|
||||
{
|
||||
"name": "Architecture",
|
||||
"files_checked": result.files_checked,
|
||||
"errors": sum(1 for v in result.violations if v.severity.value == "error"),
|
||||
"warnings": sum(1 for v in result.violations if v.severity.value == "warning"),
|
||||
"info": sum(1 for v in result.violations if v.severity.value == "info"),
|
||||
}
|
||||
)
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Architecture validator not available: {e}")
|
||||
return 0, {"name": "Architecture", "skipped": True}
|
||||
except Exception as e:
|
||||
print(f"❌ Architecture validator failed: {e}")
|
||||
return 1, {"name": "Architecture", "error": str(e)}
|
||||
|
||||
|
||||
def run_security_validator(verbose: bool = False) -> tuple[int, dict]:
|
||||
"""Run the security validator"""
|
||||
try:
|
||||
from validate_security import SecurityValidator
|
||||
|
||||
validator = SecurityValidator(verbose=verbose)
|
||||
result = validator.validate_all()
|
||||
|
||||
return (
|
||||
1 if result.has_errors() else 0,
|
||||
{
|
||||
"name": "Security",
|
||||
"files_checked": result.files_checked,
|
||||
"errors": result.error_count(),
|
||||
"warnings": result.warning_count(),
|
||||
"info": result.info_count(),
|
||||
}
|
||||
)
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Security validator not available: {e}")
|
||||
return 0, {"name": "Security", "skipped": True}
|
||||
except Exception as e:
|
||||
print(f"❌ Security validator failed: {e}")
|
||||
return 1, {"name": "Security", "error": str(e)}
|
||||
|
||||
|
||||
def run_performance_validator(verbose: bool = False) -> tuple[int, dict]:
|
||||
"""Run the performance validator"""
|
||||
try:
|
||||
from validate_performance import PerformanceValidator
|
||||
|
||||
validator = PerformanceValidator(verbose=verbose)
|
||||
result = validator.validate_all()
|
||||
|
||||
return (
|
||||
1 if result.has_errors() else 0,
|
||||
{
|
||||
"name": "Performance",
|
||||
"files_checked": result.files_checked,
|
||||
"errors": result.error_count(),
|
||||
"warnings": result.warning_count(),
|
||||
"info": result.info_count(),
|
||||
}
|
||||
)
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Performance validator not available: {e}")
|
||||
return 0, {"name": "Performance", "skipped": True}
|
||||
except Exception as e:
|
||||
print(f"❌ Performance validator failed: {e}")
|
||||
return 1, {"name": "Performance", "error": str(e)}
|
||||
|
||||
|
||||
def print_summary(results: list[dict], json_output: bool = False):
|
||||
"""Print validation summary"""
|
||||
if json_output:
|
||||
print(json.dumps({"validators": results}, indent=2))
|
||||
return
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("📊 UNIFIED VALIDATION SUMMARY")
|
||||
print("=" * 80)
|
||||
|
||||
total_errors = 0
|
||||
total_warnings = 0
|
||||
total_info = 0
|
||||
|
||||
for result in results:
|
||||
if result.get("skipped"):
|
||||
print(f"\n⏭️ {result['name']}: Skipped")
|
||||
elif result.get("error"):
|
||||
print(f"\n❌ {result['name']}: Error - {result['error']}")
|
||||
else:
|
||||
errors = result.get("errors", 0)
|
||||
warnings = result.get("warnings", 0)
|
||||
info = result.get("info", 0)
|
||||
total_errors += errors
|
||||
total_warnings += warnings
|
||||
total_info += info
|
||||
|
||||
status = "✅" if errors == 0 else "❌"
|
||||
print(f"\n{status} {result['name']}:")
|
||||
print(f" Files: {result.get('files_checked', 0)}")
|
||||
print(f" Errors: {errors}, Warnings: {warnings}, Info: {info}")
|
||||
|
||||
print("\n" + "-" * 80)
|
||||
print(f"TOTAL: {total_errors} errors, {total_warnings} warnings, {total_info} info")
|
||||
print("=" * 80)
|
||||
|
||||
if total_errors > 0:
|
||||
print("❌ VALIDATION FAILED")
|
||||
elif total_warnings > 0:
|
||||
print(f"⚠️ VALIDATION PASSED WITH {total_warnings} WARNING(S)")
|
||||
else:
|
||||
print("✅ VALIDATION PASSED")
|
||||
print("=" * 80)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Unified code validator - runs architecture, security, and performance checks",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
parser.add_argument("--architecture", action="store_true", help="Run architecture validator")
|
||||
parser.add_argument("--security", action="store_true", help="Run security validator")
|
||||
parser.add_argument("--performance", action="store_true", help="Run performance validator")
|
||||
parser.add_argument("--fail-fast", action="store_true", help="Stop on first failure")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
|
||||
parser.add_argument("--errors-only", action="store_true", help="Only show errors")
|
||||
parser.add_argument("--json", action="store_true", help="JSON output")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# If no specific validators selected, run all
|
||||
run_all = not (args.architecture or args.security or args.performance)
|
||||
|
||||
print("\n🔍 UNIFIED CODE VALIDATION")
|
||||
print("=" * 80)
|
||||
|
||||
validators = []
|
||||
if run_all or args.architecture:
|
||||
validators.append(("Architecture", run_architecture_validator))
|
||||
if run_all or args.security:
|
||||
validators.append(("Security", run_security_validator))
|
||||
if run_all or args.performance:
|
||||
validators.append(("Performance", run_performance_validator))
|
||||
|
||||
results = []
|
||||
exit_code = 0
|
||||
|
||||
for name, validator_func in validators:
|
||||
print(f"\n{'=' * 40}")
|
||||
print(f"🔍 Running {name} Validator...")
|
||||
print("=" * 40)
|
||||
|
||||
code, result = validator_func(verbose=args.verbose)
|
||||
|
||||
results.append(result)
|
||||
|
||||
if code != 0:
|
||||
exit_code = 1
|
||||
if args.fail_fast:
|
||||
print(f"\n❌ {name} validator failed. Stopping (--fail-fast)")
|
||||
break
|
||||
|
||||
print_summary(results, json_output=args.json)
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user