chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fail closed when required CI jobs or classifier outputs are incomplete."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from collections.abc import Mapping
|
||||
from typing import Final
|
||||
|
||||
BOOLEAN_FLAGS: Final[tuple[str, ...]] = (
|
||||
"docs_only",
|
||||
"runtime_changed",
|
||||
"test_changed",
|
||||
"ci_changed",
|
||||
"dependency_changed",
|
||||
"release_changed",
|
||||
"windows_full_required",
|
||||
"frontend_changed",
|
||||
"tui_changed",
|
||||
"desktop_changed",
|
||||
"python_changed",
|
||||
"platform_sensitive_changed",
|
||||
"build_wheel_required",
|
||||
"full_required",
|
||||
)
|
||||
|
||||
ALWAYS_REQUIRED_RESULTS: Final[tuple[tuple[str, str], ...]] = (
|
||||
("RESULT_CLASSIFY", "Classify changed files"),
|
||||
("RESULT_WORKFLOW_LINT", "Workflow lint"),
|
||||
("RESULT_README_LOCALE", "README locale parity"),
|
||||
)
|
||||
|
||||
|
||||
def _flag_env(name: str) -> str:
|
||||
return f"FLAG_{name.upper()}"
|
||||
|
||||
|
||||
def _read_flags(env: Mapping[str, str], errors: list[str]) -> dict[str, bool]:
|
||||
flags: dict[str, bool] = {}
|
||||
for name in BOOLEAN_FLAGS:
|
||||
raw = env.get(_flag_env(name), "")
|
||||
if raw not in {"true", "false"}:
|
||||
errors.append(f"Classifier output {name} must be exactly true or false; got {raw!r}.")
|
||||
continue
|
||||
flags[name] = raw == "true"
|
||||
return flags
|
||||
|
||||
|
||||
def _require_result(
|
||||
env: Mapping[str, str],
|
||||
errors: list[str],
|
||||
variable: str,
|
||||
label: str,
|
||||
*,
|
||||
required: bool,
|
||||
) -> None:
|
||||
result = env.get(variable, "")
|
||||
allowed = {"success"} if required else {"success", "skipped"}
|
||||
if result not in allowed:
|
||||
expectation = "succeed" if required else "be successful or intentionally skipped"
|
||||
errors.append(f"{label} must {expectation}; got {result or 'missing'}.")
|
||||
|
||||
|
||||
def check_ci_results(env: Mapping[str, str]) -> list[str]:
|
||||
"""Return gate errors; an empty list means the aggregate check may pass."""
|
||||
|
||||
errors: list[str] = []
|
||||
flags = _read_flags(env, errors)
|
||||
|
||||
for variable, label in ALWAYS_REQUIRED_RESULTS:
|
||||
_require_result(env, errors, variable, label, required=True)
|
||||
|
||||
if len(flags) != len(BOOLEAN_FLAGS):
|
||||
return errors
|
||||
|
||||
full = flags["full_required"]
|
||||
conditional_results = (
|
||||
("RESULT_FRONTEND", "Frontend build and typecheck", flags["frontend_changed"] or full),
|
||||
("RESULT_TUI", "OpenTUI package tests", flags["tui_changed"] or full),
|
||||
("RESULT_DESKTOP", "Desktop Electron unit tests", flags["desktop_changed"] or full),
|
||||
("RESULT_UBUNTU", "Ubuntu quality gate", flags["python_changed"] or full),
|
||||
(
|
||||
"RESULT_WINDOWS_SMOKE",
|
||||
"Windows compatibility smoke tests",
|
||||
flags["python_changed"]
|
||||
or flags["platform_sensitive_changed"]
|
||||
or flags["dependency_changed"]
|
||||
or flags["release_changed"]
|
||||
or full,
|
||||
),
|
||||
(
|
||||
"RESULT_WINDOWS_FULL",
|
||||
"Windows high-risk matrix",
|
||||
flags["windows_full_required"] or full,
|
||||
),
|
||||
(
|
||||
"RESULT_MACOS_RECOVERY",
|
||||
"macOS profile recovery and native no-replace tests",
|
||||
flags["platform_sensitive_changed"] or flags["desktop_changed"] or full,
|
||||
),
|
||||
(
|
||||
"RESULT_DESKTOP_RECOVERY_E2E",
|
||||
"Desktop recovery E2E matrix",
|
||||
flags["platform_sensitive_changed"] or flags["desktop_changed"] or full,
|
||||
),
|
||||
(
|
||||
"RESULT_RELEASE",
|
||||
"Release packaging contracts",
|
||||
flags["release_changed"] or full,
|
||||
),
|
||||
)
|
||||
for variable, label, required in conditional_results:
|
||||
_require_result(env, errors, variable, label, required=required)
|
||||
|
||||
if flags["platform_sensitive_changed"] and not flags["windows_full_required"]:
|
||||
errors.append("Platform-sensitive changes must require the Windows high-risk matrix.")
|
||||
|
||||
if full:
|
||||
if flags["docs_only"]:
|
||||
errors.append("A full CI run cannot be classified as docs-only.")
|
||||
for name in BOOLEAN_FLAGS:
|
||||
if name in {"docs_only", "full_required"}:
|
||||
continue
|
||||
if not flags[name]:
|
||||
errors.append(f"Full CI classification must set {name}=true.")
|
||||
|
||||
if flags["docs_only"]:
|
||||
active = [
|
||||
name
|
||||
for name in BOOLEAN_FLAGS
|
||||
if name != "docs_only" and flags[name]
|
||||
]
|
||||
if active:
|
||||
errors.append(
|
||||
"Docs-only classification cannot enable other flags: " + ", ".join(active)
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> int:
|
||||
errors = check_ci_results(os.environ)
|
||||
for variable, label in ALWAYS_REQUIRED_RESULTS:
|
||||
print(f"{label}: {os.environ.get(variable, 'missing')}")
|
||||
print(
|
||||
"Classifier flags: "
|
||||
+ " ".join(
|
||||
f"{name}={os.environ.get(_flag_env(name), 'missing')}" for name in BOOLEAN_FLAGS
|
||||
)
|
||||
)
|
||||
if not errors:
|
||||
print("All required CI results are complete and successful.")
|
||||
return 0
|
||||
for error in errors:
|
||||
print(f"ERROR: {error}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user