# requires: yaml """检查 ci.yml 的结构是否符合约定(YAML 能解析、关键任务存在、镜像只在 main 构建、没有明文密钥)。 运行:python -m pytest -q test_ci_config.py 这不能代替在真实 CI 平台上运行,只用来防止明显的配置错误。 """ import re from pathlib import Path import yaml HERE = Path(__file__).resolve().parent TEXT = (HERE / "ci.yml").read_text(encoding="utf-8") CI = yaml.safe_load(TEXT) def test_jobs_exist(): assert {"changes", "api", "ai", "web", "image"} <= set(CI["jobs"]) def test_trigger(): # 注意:PyYAML 按 YAML 1.1 把键 on 解析成 True triggers = CI.get("on", CI.get(True)) assert "pull_request" in triggers and triggers["push"]["branches"] == ["main"] def test_image_only_on_main_and_after_tests(): image = CI["jobs"]["image"] assert set(image["needs"]) == {"api", "ai", "web"} assert "refs/heads/main" in image["if"] def test_tests_run(): api_steps = " ".join(s.get("run", "") for s in CI["jobs"]["api"]["steps"]) assert "go test" in api_steps and "go vet" in api_steps web_steps = " ".join(s.get("run", "") for s in CI["jobs"]["web"]["steps"]) assert "npm ci" in web_steps and "npm run build" in web_steps def test_no_plaintext_secrets(): for line in TEXT.splitlines(): if re.search(r"(?i)(password|token|secret)\s*:", line) and "secrets." not in line: # 只有 CI 服务容器里的测试数据库密码允许明文 assert "POSTGRES_PASSWORD: test" in line, line