"""Explicit gate for historical QA evidence kept outside release source.""" from __future__ import annotations import os from pathlib import Path, PurePosixPath import pytest REPO_ROOT = Path(__file__).resolve().parents[1] QA_ROOT = (REPO_ROOT / "docs/qa").resolve() REQUIRE_EXTERNAL_QA_ENV = "YUQEI_REQUIRE_EXTERNAL_QA_EVIDENCE" def _enabled(value: str | None) -> bool: return (value or "").strip().casefold() in {"1", "true", "yes", "on"} def _safe_relative(value: str) -> Path: pure = PurePosixPath(value) if pure.is_absolute() or not pure.parts or any(part in {"", ".", ".."} for part in pure.parts): raise ValueError("external QA evidence paths must be non-empty relative paths") return Path(*pure.parts) def require_external_qa( *relative_files: str, allow_module_level: bool = False, root: Path | None = None, ) -> Path: """Return the QA root or explicitly skip/fail when required files are absent. A dedicated evidence job restores the archived ``docs/qa`` mirror into an isolated checkout and sets ``YUQEI_REQUIRE_EXTERNAL_QA_EVIDENCE=true``. The ordinary source-only gate records a skip instead of treating intentionally externalized historical evidence as release source. """ if not relative_files: raise ValueError("at least one external QA evidence file is required") evidence_root = (root or QA_ROOT).resolve() required_paths = [(evidence_root / _safe_relative(value)).resolve() for value in relative_files] if any( path == evidence_root or evidence_root not in path.parents for path in required_paths ): raise ValueError("external QA evidence path escapes its configured root") missing = [path for path in required_paths if not path.is_file()] if missing: shown = ", ".join(path.relative_to(evidence_root).as_posix() for path in missing) message = f"external historical QA evidence is missing: {shown}" if _enabled(os.environ.get(REQUIRE_EXTERNAL_QA_ENV)): pytest.fail(message, pytrace=False) pytest.skip(message, allow_module_level=allow_module_level) return evidence_root