304 lines
9.1 KiB
Python
Executable File
304 lines
9.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Quality Gates Validator
|
|
|
|
This script helps validate code against quality gates defined in the
|
|
quality-gates.md reference document.
|
|
|
|
Usage:
|
|
python validate_quality.py [--level task|phase|project] [--interactive]
|
|
|
|
Example:
|
|
python validate_quality.py --level task --interactive
|
|
python validate_quality.py --level project
|
|
"""
|
|
|
|
import sys
|
|
import argparse
|
|
from enum import Enum
|
|
|
|
|
|
class Level(Enum):
|
|
TASK = 'task'
|
|
PHASE = 'phase'
|
|
PROJECT = 'project'
|
|
|
|
|
|
class Severity(Enum):
|
|
CRITICAL = ''
|
|
WARNING = ''
|
|
INFO = ''
|
|
|
|
|
|
# Quality Gate Definitions
|
|
TASK_GATES = {
|
|
'Functional Requirements': [
|
|
'All specified outputs delivered',
|
|
'Functionality works as described',
|
|
'Edge cases handled',
|
|
'Error cases handled gracefully',
|
|
'No regression in existing functionality'
|
|
],
|
|
'Code Quality': [
|
|
'Code is readable and self-documenting',
|
|
'Variable/function names are meaningful',
|
|
'No magic numbers or strings',
|
|
'No commented-out code',
|
|
'Consistent code style with project'
|
|
],
|
|
'DRY': [
|
|
'No duplicated logic',
|
|
'Shared functionality extracted to utilities',
|
|
'Constants defined once',
|
|
'No copy-paste code blocks'
|
|
],
|
|
'Testing': [
|
|
'Unit tests written for new code',
|
|
'Tests cover happy path',
|
|
'Tests cover edge cases',
|
|
'Tests cover error conditions',
|
|
'All tests pass'
|
|
],
|
|
'Documentation': [
|
|
'Complex logic has explanatory comments',
|
|
'Public APIs documented',
|
|
'README updated if needed',
|
|
'Breaking changes documented'
|
|
]
|
|
}
|
|
|
|
PHASE_GATES = {
|
|
'Integration': [
|
|
'All components integrate correctly',
|
|
'Data flows between components as expected',
|
|
'No integration bugs',
|
|
'APIs between components are clean',
|
|
'Interfaces are well-defined'
|
|
],
|
|
'CLEAN Principles': [
|
|
'Clear: Code is easy to understand',
|
|
'Limited: Functions have single responsibility',
|
|
'Expressive: Naming reveals intent',
|
|
'Abstracted: Proper level of abstraction',
|
|
'Neat: Organized, well-structured code'
|
|
],
|
|
'Performance': [
|
|
'No obvious performance issues',
|
|
'Efficient algorithms used',
|
|
'No unnecessary computations',
|
|
'Resources properly managed',
|
|
'Meets stated performance requirements'
|
|
],
|
|
'Security': [
|
|
'No injection vulnerabilities',
|
|
'Input validation in place',
|
|
'Output encoding where needed',
|
|
'Authentication/authorization checked',
|
|
'Sensitive data not logged or exposed',
|
|
'Dependencies have no known vulnerabilities'
|
|
]
|
|
}
|
|
|
|
PROJECT_GATES = {
|
|
'SOLID - Single Responsibility': [
|
|
'Each class/module has one reason to change',
|
|
'Each function does one thing well',
|
|
'No god objects or god functions',
|
|
'Responsibilities clearly separated'
|
|
],
|
|
'SOLID - Open/Closed': [
|
|
'Open for extension (can add new behavior)',
|
|
'Closed for modification',
|
|
'Use abstractions for extension points',
|
|
'Configuration over hardcoding'
|
|
],
|
|
'SOLID - Liskov Substitution': [
|
|
'Subtypes can replace base types without breaking',
|
|
'Derived classes don\'t weaken preconditions',
|
|
'Inheritance is "is-a" relationship'
|
|
],
|
|
'SOLID - Interface Segregation': [
|
|
'Interfaces are focused and cohesive',
|
|
'No client forced to depend on unused methods',
|
|
'Many small interfaces over one large interface'
|
|
],
|
|
'SOLID - Dependency Inversion': [
|
|
'High-level modules don\'t depend on low-level modules',
|
|
'Both depend on abstractions',
|
|
'Dependencies injected, not hardcoded'
|
|
],
|
|
'Testing Coverage': [
|
|
'Unit test coverage meets threshold',
|
|
'Integration tests for key workflows',
|
|
'E2E tests for critical user paths',
|
|
'All tests passing consistently',
|
|
'No flaky tests'
|
|
],
|
|
'Documentation Completeness': [
|
|
'README is current and accurate',
|
|
'API documentation complete',
|
|
'Architecture documented',
|
|
'Setup instructions clear',
|
|
'Troubleshooting guide available'
|
|
],
|
|
'Production Readiness': [
|
|
'No breaking changes or migration guide provided',
|
|
'Error handling comprehensive',
|
|
'Logging appropriate',
|
|
'Configuration externalized',
|
|
'Secrets properly managed'
|
|
]
|
|
}
|
|
|
|
|
|
def get_gates_for_level(level):
|
|
"""Get quality gates for specified level."""
|
|
if level == Level.TASK:
|
|
return TASK_GATES
|
|
elif level == Level.PHASE:
|
|
return {**TASK_GATES, **PHASE_GATES}
|
|
elif level == Level.PROJECT:
|
|
return {**TASK_GATES, **PHASE_GATES, **PROJECT_GATES}
|
|
|
|
|
|
def interactive_validation(gates):
|
|
"""Run interactive validation."""
|
|
results = {}
|
|
total_checks = sum(len(checks) for checks in gates.values())
|
|
current = 0
|
|
|
|
print("\n" + "="*70)
|
|
print("[SEARCH] Quality Gates Validation")
|
|
print("="*70)
|
|
print(f"\nTotal checks: {total_checks}")
|
|
print("\nFor each check, respond: y (yes/pass), n (no/fail), s (skip)\n")
|
|
|
|
for category, checks in gates.items():
|
|
print(f"\n{''*70}")
|
|
print(f" {category}")
|
|
print(f"{''*70}")
|
|
|
|
category_results = []
|
|
|
|
for check in checks:
|
|
current += 1
|
|
while True:
|
|
response = input(f" [{current}/{total_checks}] {check}? [y/n/s]: ").lower().strip()
|
|
|
|
if response in ['y', 'yes']:
|
|
print(f" [OK] Pass")
|
|
category_results.append(('pass', check))
|
|
break
|
|
elif response in ['n', 'no']:
|
|
print(f" [ERROR] Fail")
|
|
category_results.append(('fail', check))
|
|
break
|
|
elif response in ['s', 'skip']:
|
|
print(f" Skip")
|
|
category_results.append(('skip', check))
|
|
break
|
|
else:
|
|
print(" Invalid input. Use y/n/s")
|
|
|
|
results[category] = category_results
|
|
|
|
return results
|
|
|
|
|
|
def print_summary(results, level):
|
|
"""Print validation summary."""
|
|
total_pass = sum(1 for cat in results.values() for status, _ in cat if status == 'pass')
|
|
total_fail = sum(1 for cat in results.values() for status, _ in cat if status == 'fail')
|
|
total_skip = sum(1 for cat in results.values() for status, _ in cat if status == 'skip')
|
|
total_checks = total_pass + total_fail + total_skip
|
|
|
|
print("\n" + "="*70)
|
|
print("[INFO] VALIDATION SUMMARY")
|
|
print("="*70)
|
|
|
|
print(f"\nLevel: {level.value.upper()}")
|
|
print(f"\nResults:")
|
|
print(f" [OK] Passed: {total_pass}/{total_checks}")
|
|
print(f" [ERROR] Failed: {total_fail}/{total_checks}")
|
|
print(f" Skipped: {total_skip}/{total_checks}")
|
|
|
|
if total_fail > 0:
|
|
print(f"\n FAILED CHECKS:")
|
|
for category, checks in results.items():
|
|
failed = [(status, check) for status, check in checks if status == 'fail']
|
|
if failed:
|
|
print(f"\n {category}:")
|
|
for _, check in failed:
|
|
print(f" [ERROR] {check}")
|
|
|
|
print("\n" + "="*70)
|
|
|
|
if total_fail == 0 and total_skip == 0:
|
|
print(" ALL QUALITY GATES PASSED!")
|
|
print("="*70)
|
|
return True
|
|
elif total_fail == 0:
|
|
print(f"[WARNING] All checked gates passed, but {total_skip} checks were skipped.")
|
|
print("="*70)
|
|
return True
|
|
else:
|
|
print(f"[ERROR] {total_fail} QUALITY GATES FAILED")
|
|
print("[TOOL] Please address failed checks before proceeding.")
|
|
print("="*70)
|
|
return False
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description='Validate code against quality gates',
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
python validate_quality.py --level task --interactive
|
|
python validate_quality.py --level project
|
|
python validate_quality.py --level phase --interactive
|
|
"""
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--level',
|
|
type=str,
|
|
choices=['task', 'phase', 'project'],
|
|
default='task',
|
|
help='Validation level (default: task)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--interactive',
|
|
action='store_true',
|
|
help='Run interactive validation'
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
level = Level(args.level)
|
|
gates = get_gates_for_level(level)
|
|
|
|
if args.interactive:
|
|
results = interactive_validation(gates)
|
|
success = print_summary(results, level)
|
|
sys.exit(0 if success else 1)
|
|
else:
|
|
# Non-interactive mode - just print checklist
|
|
print(f"\n{'='*70}")
|
|
print(f"Quality Gates Checklist - {level.value.upper()} Level")
|
|
print(f"{'='*70}\n")
|
|
|
|
for category, checks in gates.items():
|
|
print(f"\n{category}:")
|
|
for check in checks:
|
|
print(f" [ ] {check}")
|
|
|
|
print(f"\n{'='*70}")
|
|
print("Run with --interactive flag for guided validation")
|
|
print(f"{'='*70}\n")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|