Files
sandbox/ci/check_pr_policies.py
T
torsten 58c3cd0559
sandbox/pipeline/pr-main There was a failure building this commit
Fix PR policy target ref resolution
2026-08-04 19:44:36 +02:00

129 lines
3.5 KiB
Python

#!/usr/bin/env python3
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(".").resolve()
TEST_DIR = ROOT / "tests"
CPP_EXTENSIONS = {".c", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"}
TEST_PATTERNS = [
"test_{name}.cpp",
"test_{name}.h",
"{name}_test.cpp",
"{name}_test.h",
"{name}test.cpp",
"{name}test.h",
"{name}gtest.cpp",
"{name}gtest.h",
]
def run(cmd: str) -> str:
return subprocess.check_output(cmd, shell=True, text=True).strip()
def git_ref_exists(ref: str) -> bool:
try:
subprocess.check_call(
f"git rev-parse --verify --quiet '{ref}'",
shell=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
return True
except subprocess.CalledProcessError:
return False
def resolve_target_ref() -> str:
raw = os.environ.get("CHANGE_TARGET", "").strip()
candidates = []
if raw:
candidates.append(raw)
if not raw.startswith("origin/"):
candidates.append(f"origin/{raw}")
candidates.extend([
"origin/main",
"main",
])
seen = set()
for ref in candidates:
if ref and ref not in seen:
seen.add(ref)
if git_ref_exists(ref):
return ref
print("ERROR: Kein gültiger Ziel-Branch-Ref gefunden. Geprüft:", ", ".join(candidates))
sys.exit(1)
def changed_files():
target = resolve_target_ref()
print(f"Vergleiche gegen Ziel-Ref: {target}")
out = run(f"git diff --name-only {target}...HEAD")
return [line.strip() for line in out.splitlines() if line.strip()]
def is_cpp_file(path: Path) -> bool:
return path.suffix.lower() in CPP_EXTENSIONS
def require_clang_format():
if not (ROOT / ".clang-format").exists():
print("ERROR: .clang-format fehlt im Repository-Root.")
sys.exit(1)
def check_format(path: Path):
subprocess.check_call(f"clang-format --dry-run --Werror '{path}'", shell=True)
def candidate_names(path: Path):
stem = path.stem.lower()
names = {stem}
for suffix in ["_p", "_impl", "_private", "_priv", "_ui"]:
if stem.endswith(suffix):
names.add(stem[:-len(suffix)])
return sorted(n for n in names if n)
def has_related_test(path: Path) -> bool:
if not TEST_DIR.exists():
return False
test_files = [p for p in TEST_DIR.rglob("*") if p.is_file()]
test_names = [p.name.lower() for p in test_files]
for name in candidate_names(path):
for pattern in TEST_PATTERNS:
if pattern.format(name=name).lower() in test_names:
return True
for test_name in test_names:
if name in test_name and ("test" in test_name or "gtest" in test_name):
return True
return False
def main():
require_clang_format()
files = changed_files()
if not files:
print("Keine geänderten Dateien erkannt.")
return
for rel in files:
path = ROOT / rel
if not path.exists():
continue
if is_cpp_file(path):
print(f"Prüfe: {rel}")
try:
check_format(path)
except subprocess.CalledProcessError:
print(f"ERROR: Formatfehler in {rel}")
sys.exit(1)
if not has_related_test(path):
print(f"ERROR: Kein passender Test unter tests/ für {rel} gefunden.")
sys.exit(1)
print("Policy-Checks erfolgreich.")
if __name__ == "__main__":
main()