153 lines
4.3 KiB
Python
Executable File
153 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(".").resolve()
|
|
TEST_DIRS = [
|
|
ROOT / "tests",
|
|
ROOT / "test",
|
|
ROOT / "qt6-after" / "qt6-migrated-benchmark" / "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 is_pull_request_build() -> bool:
|
|
return bool(os.environ.get("CHANGE_ID"))
|
|
|
|
def resolve_target_ref() -> str | None:
|
|
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
|
|
|
|
return None
|
|
|
|
def changed_files():
|
|
target = resolve_target_ref()
|
|
|
|
if target:
|
|
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()]
|
|
|
|
if is_pull_request_build():
|
|
print("ERROR: Kein gültiger Ziel-Branch-Ref für PR-Build gefunden.")
|
|
sys.exit(1)
|
|
|
|
if git_ref_exists("HEAD~1"):
|
|
print("Kein Ziel-Branch-Ref gefunden, verwende HEAD~1 für Branch-Build.")
|
|
out = run("git diff --name-only HEAD~1..HEAD")
|
|
return [line.strip() for line in out.splitlines() if line.strip()]
|
|
|
|
print("Keine Vergleichsbasis gefunden, behandle alle versionierten Dateien als geändert.")
|
|
out = run("git ls-files")
|
|
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 iter_test_files():
|
|
for test_dir in TEST_DIRS:
|
|
if test_dir.exists():
|
|
yield from (p for p in test_dir.rglob("*") if p.is_file())
|
|
|
|
def has_related_test(path: Path) -> bool:
|
|
test_files = list(iter_test_files())
|
|
if not test_files:
|
|
return False
|
|
|
|
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/, test/ oder qt6-after/qt6-migrated-benchmark/tests für {rel} gefunden.")
|
|
sys.exit(1)
|
|
|
|
print("Policy-Checks erfolgreich.")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|