| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- #!/usr/bin/env python3
- """
- 最终系统验证脚本
- 此脚本执行完整的系统验证,包括:
- 1. 代码质量检查
- 2. 测试执行
- 3. 文档完整性检查
- 4. API 向后兼容性验证
- 5. 性能基准测试(可选)
- """
- import subprocess
- import sys
- from pathlib import Path
- from typing import List, Tuple
- import json
- class Colors:
- """终端颜色"""
- GREEN = '\033[92m'
- YELLOW = '\033[93m'
- RED = '\033[91m'
- BLUE = '\033[94m'
- END = '\033[0m'
- BOLD = '\033[1m'
- def print_header(text: str):
- """打印标题"""
- print(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.END}")
- print(f"{Colors.BOLD}{Colors.BLUE}{text}{Colors.END}")
- print(f"{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.END}\n")
- def print_success(text: str):
- """打印成功消息"""
- print(f"{Colors.GREEN}✓ {text}{Colors.END}")
- def print_warning(text: str):
- """打印警告消息"""
- print(f"{Colors.YELLOW}⚠ {text}{Colors.END}")
- def print_error(text: str):
- """打印错误消息"""
- print(f"{Colors.RED}✗ {text}{Colors.END}")
- def run_command(cmd: List[str], description: str) -> Tuple[bool, str]:
- """
- 运行命令并返回结果
-
- Args:
- cmd: 命令列表
- description: 命令描述
-
- Returns:
- (成功标志, 输出)
- """
- print(f"Running: {description}...")
- try:
- result = subprocess.run(
- cmd,
- capture_output=True,
- text=True,
- timeout=300 # 5 分钟超时
- )
-
- if result.returncode == 0:
- print_success(f"{description} passed")
- return True, result.stdout
- else:
- print_error(f"{description} failed")
- print(f"Error output:\n{result.stderr}")
- return False, result.stderr
-
- except subprocess.TimeoutExpired:
- print_error(f"{description} timed out")
- return False, "Timeout"
-
- except Exception as e:
- print_error(f"{description} error: {e}")
- return False, str(e)
- def check_code_quality() -> bool:
- """检查代码质量"""
- print_header("1. Code Quality Checks")
-
- checks = [
- (['flake8', 'src', 'tests', '--count', '--select=E9,F63,F7,F82', '--show-source', '--statistics'],
- "Flake8 syntax check"),
- (['black', '--check', 'src', 'tests'],
- "Black formatting check"),
- (['isort', '--check-only', 'src', 'tests'],
- "isort import sorting check"),
- ]
-
- all_passed = True
- for cmd, desc in checks:
- success, _ = run_command(cmd, desc)
- if not success:
- all_passed = False
- print_warning(f" Tip: Run '{' '.join(cmd[:-2])}' to fix")
-
- return all_passed
- def run_tests() -> bool:
- """运行测试"""
- print_header("2. Test Execution")
-
- # 运行单元测试
- success, output = run_command(
- ['pytest', 'tests/unit', '-v', '--tb=short'],
- "Unit tests"
- )
-
- if not success:
- return False
-
- # 运行集成测试(如果存在)
- if Path('tests/integration').exists():
- success, output = run_command(
- ['pytest', 'tests/integration', '-v', '--tb=short'],
- "Integration tests"
- )
- if not success:
- print_warning("Integration tests failed (may require services)")
-
- return True
- def check_test_coverage() -> bool:
- """检查测试覆盖率"""
- print_header("3. Test Coverage")
-
- success, output = run_command(
- ['pytest', '--cov=src', '--cov-report=term', '--cov-report=json'],
- "Coverage analysis"
- )
-
- if not success:
- return False
-
- # 读取覆盖率报告
- try:
- with open('coverage.json', 'r') as f:
- coverage_data = json.load(f)
-
- total_coverage = coverage_data['totals']['percent_covered']
- print(f"\nTotal coverage: {total_coverage:.2f}%")
-
- # 检查目标覆盖率
- target_coverage = 80.0
- if total_coverage >= target_coverage:
- print_success(f"Coverage meets target ({target_coverage}%)")
- return True
- else:
- print_warning(f"Coverage below target ({target_coverage}%)")
- return False
-
- except Exception as e:
- print_error(f"Failed to read coverage report: {e}")
- return False
- def check_documentation() -> bool:
- """检查文档完整性"""
- print_header("4. Documentation Completeness")
-
- required_docs = [
- 'README.md',
- 'docs/architecture.md',
- 'docs/api.md',
- 'docs/deployment.md',
- 'docs/development.md',
- 'docs/configuration.md',
- 'docs/logging.md',
- 'docs/ci-cd.md',
- ]
-
- all_exist = True
- for doc in required_docs:
- path = Path(doc)
- if path.exists():
- print_success(f"{doc} exists")
- else:
- print_error(f"{doc} missing")
- all_exist = False
-
- return all_exist
- def check_api_compatibility() -> bool:
- """检查 API 向后兼容性"""
- print_header("5. API Backward Compatibility")
-
- # 检查 legacy adapter 是否存在
- legacy_adapter = Path('src/presentation/api/legacy_adapter.py')
- if legacy_adapter.exists():
- print_success("Legacy adapter exists")
- else:
- print_warning("Legacy adapter not found")
- return False
-
- # 检查废弃警告
- deprecated_file = Path('src/api/DEPRECATED.md')
- if deprecated_file.exists():
- print_success("Deprecation notice exists")
- else:
- print_warning("Deprecation notice not found")
-
- return True
- def check_docker_config() -> bool:
- """检查 Docker 配置"""
- print_header("6. Docker Configuration")
-
- docker_files = [
- 'docker/Dockerfile',
- 'docker/docker-compose.yml',
- 'docker/docker-compose.dev.yml',
- 'alembic.ini',
- 'alembic/env.py',
- ]
-
- all_exist = True
- for file_path in docker_files:
- path = Path(file_path)
- if path.exists():
- print_success(f"{file_path} exists")
- else:
- print_error(f"{file_path} missing")
- all_exist = False
-
- return all_exist
- def generate_report(results: dict) -> str:
- """生成验证报告"""
- report_lines = [
- "\n" + "=" * 60,
- "FINAL VERIFICATION REPORT",
- "=" * 60,
- ""
- ]
-
- total_checks = len(results)
- passed_checks = sum(1 for v in results.values() if v)
-
- for check_name, passed in results.items():
- status = "✓ PASS" if passed else "✗ FAIL"
- color = Colors.GREEN if passed else Colors.RED
- report_lines.append(f"{color}{status}{Colors.END} - {check_name}")
-
- report_lines.extend([
- "",
- f"Total: {passed_checks}/{total_checks} checks passed",
- ""
- ])
-
- if passed_checks == total_checks:
- report_lines.append(f"{Colors.GREEN}{Colors.BOLD}✓ ALL CHECKS PASSED!{Colors.END}")
- report_lines.append("")
- report_lines.append("The system is ready for deployment.")
- else:
- report_lines.append(f"{Colors.RED}{Colors.BOLD}✗ SOME CHECKS FAILED{Colors.END}")
- report_lines.append("")
- report_lines.append("Please fix the issues before deployment.")
-
- report_lines.append("=" * 60)
-
- return "\n".join(report_lines)
- def main():
- """主函数"""
- print(f"{Colors.BOLD}RAG System - Final Verification{Colors.END}")
- print(f"This script will verify the system is ready for deployment.\n")
-
- # 执行所有检查
- results = {}
-
- try:
- results['Code Quality'] = check_code_quality()
- results['Tests'] = run_tests()
- results['Coverage'] = check_test_coverage()
- results['Documentation'] = check_documentation()
- results['API Compatibility'] = check_api_compatibility()
- results['Docker Config'] = check_docker_config()
-
- except KeyboardInterrupt:
- print("\n\nVerification interrupted by user")
- sys.exit(1)
-
- # 生成并打印报告
- report = generate_report(results)
- print(report)
-
- # 保存报告到文件
- report_file = Path('verification_report.txt')
- with open(report_file, 'w', encoding='utf-8') as f:
- # 移除颜色代码
- clean_report = report
- for color in [Colors.GREEN, Colors.YELLOW, Colors.RED, Colors.BLUE, Colors.END, Colors.BOLD]:
- clean_report = clean_report.replace(color, '')
- f.write(clean_report)
-
- print(f"\nReport saved to: {report_file}")
-
- # 返回退出码
- all_passed = all(results.values())
- sys.exit(0 if all_passed else 1)
- if __name__ == '__main__':
- main()
|