45 lines
1.5 KiB
Python
45 lines
1.5 KiB
Python
# 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
|