日常更新
This commit is contained in:
107
ansible/tools/validate_matrix_playbooks.py
Normal file
107
ansible/tools/validate_matrix_playbooks.py
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""校验执行域 verify/doc/files 一致性。
|
||||
|
||||
该脚本是“离线门禁”的一部分:发现 doc_id 三元契约不一致时应 fail-fast,
|
||||
并输出可定位的冲突清单(doc_id + 路径集合)。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
VERIFY_DIR = ROOT / "ansible" / "playbooks" / "verify"
|
||||
DOCS_DIR = ROOT / "docs"
|
||||
EXEC_ID_RE = re.compile(r"^(0[1-9]|[1-9][0-9])-(0[1-9]|[1-9][0-9])$")
|
||||
|
||||
|
||||
def is_exec_domain(doc_id: str) -> bool:
|
||||
return EXEC_ID_RE.fullmatch(doc_id) is not None
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not VERIFY_DIR.is_dir() or not DOCS_DIR.is_dir():
|
||||
print("ERR: verify/docs 目录缺失", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
# --- scan verify playbooks (.yml/.yaml) ---
|
||||
verify_by_doc_id: dict[str, list[Path]] = {}
|
||||
invalid_verify_names: list[str] = []
|
||||
|
||||
for p in sorted(VERIFY_DIR.iterdir()):
|
||||
if not p.is_file():
|
||||
continue
|
||||
if p.name.startswith("_"):
|
||||
# private helpers are allowed but not part of doc_id contract
|
||||
continue
|
||||
if p.suffix not in {".yml", ".yaml"}:
|
||||
continue
|
||||
|
||||
# Only accept <doc_id>.yml|yaml
|
||||
stem = p.stem
|
||||
if len(stem) != len("00-00") or stem[2:3] != "-":
|
||||
invalid_verify_names.append(p.name)
|
||||
continue
|
||||
|
||||
if not is_exec_domain(stem):
|
||||
invalid_verify_names.append(p.name)
|
||||
continue
|
||||
|
||||
verify_by_doc_id.setdefault(stem, []).append(p)
|
||||
|
||||
# Same doc_id with multiple verify entrypoints is a hard conflict (EC1).
|
||||
verify_conflicts: dict[str, list[str]] = {}
|
||||
for did, paths in verify_by_doc_id.items():
|
||||
if len(paths) > 1:
|
||||
verify_conflicts[did] = [str(p.relative_to(ROOT)) for p in sorted(paths)]
|
||||
|
||||
missing_docs: list[str] = []
|
||||
missing_files_dir: list[str] = []
|
||||
weak_doc_exec_refs: list[str] = []
|
||||
multi_docs: dict[str, list[str]] = {}
|
||||
|
||||
for did in sorted(verify_by_doc_id.keys()):
|
||||
matches = sorted(DOCS_DIR.glob(f"{did}-*.md"))
|
||||
if not matches:
|
||||
missing_docs.append(did)
|
||||
continue
|
||||
if len(matches) > 1:
|
||||
multi_docs[did] = [str(p.relative_to(ROOT)) for p in matches]
|
||||
doc = matches[0]
|
||||
content = doc.read_text(encoding="utf-8", errors="ignore")
|
||||
if f"ansible/files/{did}/" not in content and "```yaml" in content:
|
||||
weak_doc_exec_refs.append(did)
|
||||
# Execution-domain doc_id must always have a files truth-source directory (ADR-002 / R4).
|
||||
if not (ROOT / "ansible" / "files" / did).is_dir():
|
||||
missing_files_dir.append(did)
|
||||
|
||||
if invalid_verify_names:
|
||||
print(f"ERR: verify 仅允许执行域命名: {sorted(invalid_verify_names)}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
if verify_conflicts:
|
||||
print("ERR: verify 入口冲突(同一 doc_id 多个入口,必须 fail-fast):", file=sys.stderr)
|
||||
for did in sorted(verify_conflicts.keys()):
|
||||
paths = verify_conflicts[did]
|
||||
print(f" - {did}: {paths}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
if missing_docs:
|
||||
print(f"ERR: 缺少 docs/<doc_id>-*.md: {missing_docs}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
if multi_docs:
|
||||
print("ERR: docs 命名冲突(同一 doc_id 匹配到多篇 docs/<doc_id>-*.md):", file=sys.stderr)
|
||||
for did in sorted(multi_docs.keys()):
|
||||
print(f" - {did}: {multi_docs[did]}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
if missing_files_dir:
|
||||
print(f"ERR: 缺少 ansible/files/<doc_id>/ 目录: {missing_files_dir}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
if weak_doc_exec_refs:
|
||||
print(f"ERR: 文档 YAML 未映射 ansible/files/<doc_id>/ 真源: {weak_doc_exec_refs}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
print(f"[OK] 执行域 verify/doc/files 一致性通过({len(verify_by_doc_id)} 条)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user