final_verification.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. #!/usr/bin/env python3
  2. """
  3. 最终系统验证脚本
  4. 此脚本执行完整的系统验证,包括:
  5. 1. 代码质量检查
  6. 2. 测试执行
  7. 3. 文档完整性检查
  8. 4. API 向后兼容性验证
  9. 5. 性能基准测试(可选)
  10. """
  11. import subprocess
  12. import sys
  13. from pathlib import Path
  14. from typing import List, Tuple
  15. import json
  16. class Colors:
  17. """终端颜色"""
  18. GREEN = '\033[92m'
  19. YELLOW = '\033[93m'
  20. RED = '\033[91m'
  21. BLUE = '\033[94m'
  22. END = '\033[0m'
  23. BOLD = '\033[1m'
  24. def print_header(text: str):
  25. """打印标题"""
  26. print(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.END}")
  27. print(f"{Colors.BOLD}{Colors.BLUE}{text}{Colors.END}")
  28. print(f"{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.END}\n")
  29. def print_success(text: str):
  30. """打印成功消息"""
  31. print(f"{Colors.GREEN}✓ {text}{Colors.END}")
  32. def print_warning(text: str):
  33. """打印警告消息"""
  34. print(f"{Colors.YELLOW}⚠ {text}{Colors.END}")
  35. def print_error(text: str):
  36. """打印错误消息"""
  37. print(f"{Colors.RED}✗ {text}{Colors.END}")
  38. def run_command(cmd: List[str], description: str) -> Tuple[bool, str]:
  39. """
  40. 运行命令并返回结果
  41. Args:
  42. cmd: 命令列表
  43. description: 命令描述
  44. Returns:
  45. (成功标志, 输出)
  46. """
  47. print(f"Running: {description}...")
  48. try:
  49. result = subprocess.run(
  50. cmd,
  51. capture_output=True,
  52. text=True,
  53. timeout=300 # 5 分钟超时
  54. )
  55. if result.returncode == 0:
  56. print_success(f"{description} passed")
  57. return True, result.stdout
  58. else:
  59. print_error(f"{description} failed")
  60. print(f"Error output:\n{result.stderr}")
  61. return False, result.stderr
  62. except subprocess.TimeoutExpired:
  63. print_error(f"{description} timed out")
  64. return False, "Timeout"
  65. except Exception as e:
  66. print_error(f"{description} error: {e}")
  67. return False, str(e)
  68. def check_code_quality() -> bool:
  69. """检查代码质量"""
  70. print_header("1. Code Quality Checks")
  71. checks = [
  72. (['flake8', 'src', 'tests', '--count', '--select=E9,F63,F7,F82', '--show-source', '--statistics'],
  73. "Flake8 syntax check"),
  74. (['black', '--check', 'src', 'tests'],
  75. "Black formatting check"),
  76. (['isort', '--check-only', 'src', 'tests'],
  77. "isort import sorting check"),
  78. ]
  79. all_passed = True
  80. for cmd, desc in checks:
  81. success, _ = run_command(cmd, desc)
  82. if not success:
  83. all_passed = False
  84. print_warning(f" Tip: Run '{' '.join(cmd[:-2])}' to fix")
  85. return all_passed
  86. def run_tests() -> bool:
  87. """运行测试"""
  88. print_header("2. Test Execution")
  89. # 运行单元测试
  90. success, output = run_command(
  91. ['pytest', 'tests/unit', '-v', '--tb=short'],
  92. "Unit tests"
  93. )
  94. if not success:
  95. return False
  96. # 运行集成测试(如果存在)
  97. if Path('tests/integration').exists():
  98. success, output = run_command(
  99. ['pytest', 'tests/integration', '-v', '--tb=short'],
  100. "Integration tests"
  101. )
  102. if not success:
  103. print_warning("Integration tests failed (may require services)")
  104. return True
  105. def check_test_coverage() -> bool:
  106. """检查测试覆盖率"""
  107. print_header("3. Test Coverage")
  108. success, output = run_command(
  109. ['pytest', '--cov=src', '--cov-report=term', '--cov-report=json'],
  110. "Coverage analysis"
  111. )
  112. if not success:
  113. return False
  114. # 读取覆盖率报告
  115. try:
  116. with open('coverage.json', 'r') as f:
  117. coverage_data = json.load(f)
  118. total_coverage = coverage_data['totals']['percent_covered']
  119. print(f"\nTotal coverage: {total_coverage:.2f}%")
  120. # 检查目标覆盖率
  121. target_coverage = 80.0
  122. if total_coverage >= target_coverage:
  123. print_success(f"Coverage meets target ({target_coverage}%)")
  124. return True
  125. else:
  126. print_warning(f"Coverage below target ({target_coverage}%)")
  127. return False
  128. except Exception as e:
  129. print_error(f"Failed to read coverage report: {e}")
  130. return False
  131. def check_documentation() -> bool:
  132. """检查文档完整性"""
  133. print_header("4. Documentation Completeness")
  134. required_docs = [
  135. 'README.md',
  136. 'docs/architecture.md',
  137. 'docs/api.md',
  138. 'docs/deployment.md',
  139. 'docs/development.md',
  140. 'docs/configuration.md',
  141. 'docs/logging.md',
  142. 'docs/ci-cd.md',
  143. ]
  144. all_exist = True
  145. for doc in required_docs:
  146. path = Path(doc)
  147. if path.exists():
  148. print_success(f"{doc} exists")
  149. else:
  150. print_error(f"{doc} missing")
  151. all_exist = False
  152. return all_exist
  153. def check_api_compatibility() -> bool:
  154. """检查 API 向后兼容性"""
  155. print_header("5. API Backward Compatibility")
  156. # 检查 legacy adapter 是否存在
  157. legacy_adapter = Path('src/presentation/api/legacy_adapter.py')
  158. if legacy_adapter.exists():
  159. print_success("Legacy adapter exists")
  160. else:
  161. print_warning("Legacy adapter not found")
  162. return False
  163. # 检查废弃警告
  164. deprecated_file = Path('src/api/DEPRECATED.md')
  165. if deprecated_file.exists():
  166. print_success("Deprecation notice exists")
  167. else:
  168. print_warning("Deprecation notice not found")
  169. return True
  170. def check_docker_config() -> bool:
  171. """检查 Docker 配置"""
  172. print_header("6. Docker Configuration")
  173. docker_files = [
  174. 'docker/Dockerfile',
  175. 'docker/docker-compose.yml',
  176. 'docker/docker-compose.dev.yml',
  177. 'alembic.ini',
  178. 'alembic/env.py',
  179. ]
  180. all_exist = True
  181. for file_path in docker_files:
  182. path = Path(file_path)
  183. if path.exists():
  184. print_success(f"{file_path} exists")
  185. else:
  186. print_error(f"{file_path} missing")
  187. all_exist = False
  188. return all_exist
  189. def generate_report(results: dict) -> str:
  190. """生成验证报告"""
  191. report_lines = [
  192. "\n" + "=" * 60,
  193. "FINAL VERIFICATION REPORT",
  194. "=" * 60,
  195. ""
  196. ]
  197. total_checks = len(results)
  198. passed_checks = sum(1 for v in results.values() if v)
  199. for check_name, passed in results.items():
  200. status = "✓ PASS" if passed else "✗ FAIL"
  201. color = Colors.GREEN if passed else Colors.RED
  202. report_lines.append(f"{color}{status}{Colors.END} - {check_name}")
  203. report_lines.extend([
  204. "",
  205. f"Total: {passed_checks}/{total_checks} checks passed",
  206. ""
  207. ])
  208. if passed_checks == total_checks:
  209. report_lines.append(f"{Colors.GREEN}{Colors.BOLD}✓ ALL CHECKS PASSED!{Colors.END}")
  210. report_lines.append("")
  211. report_lines.append("The system is ready for deployment.")
  212. else:
  213. report_lines.append(f"{Colors.RED}{Colors.BOLD}✗ SOME CHECKS FAILED{Colors.END}")
  214. report_lines.append("")
  215. report_lines.append("Please fix the issues before deployment.")
  216. report_lines.append("=" * 60)
  217. return "\n".join(report_lines)
  218. def main():
  219. """主函数"""
  220. print(f"{Colors.BOLD}RAG System - Final Verification{Colors.END}")
  221. print(f"This script will verify the system is ready for deployment.\n")
  222. # 执行所有检查
  223. results = {}
  224. try:
  225. results['Code Quality'] = check_code_quality()
  226. results['Tests'] = run_tests()
  227. results['Coverage'] = check_test_coverage()
  228. results['Documentation'] = check_documentation()
  229. results['API Compatibility'] = check_api_compatibility()
  230. results['Docker Config'] = check_docker_config()
  231. except KeyboardInterrupt:
  232. print("\n\nVerification interrupted by user")
  233. sys.exit(1)
  234. # 生成并打印报告
  235. report = generate_report(results)
  236. print(report)
  237. # 保存报告到文件
  238. report_file = Path('verification_report.txt')
  239. with open(report_file, 'w', encoding='utf-8') as f:
  240. # 移除颜色代码
  241. clean_report = report
  242. for color in [Colors.GREEN, Colors.YELLOW, Colors.RED, Colors.BLUE, Colors.END, Colors.BOLD]:
  243. clean_report = clean_report.replace(color, '')
  244. f.write(clean_report)
  245. print(f"\nReport saved to: {report_file}")
  246. # 返回退出码
  247. all_passed = all(results.values())
  248. sys.exit(0 if all_passed else 1)
  249. if __name__ == '__main__':
  250. main()