#!/usr/bin/env python3 """Pre-Commit-Pruefung: faehrt die Stufe-1-Checks automatisch vor jedem Commit. ⚠ WOZU. Am 2026-08-07 habe ich `py_compile`, `pytest` und `check_nfalle` rund fuenfzehnmal von Hand gestartet — und trotzdem gingen zwei Fehler durch, weil ich sie im falschen Moment aufrief: die `\\n`-Falle fiel erst beim naechsten Aufruf auf, die beiden `log.`-Fehler in `core/trader.py` lagen MONATE drin (einer seit dem Initial-Commit). Ein Hook vergisst das nicht. WAS GEPRUEFT WIRD (~3–4 s): 1. `tools/check_nfalle.py` — kompiliert alle .py, ruff (F821/E999), JS-String-Literale, die \\n-Falle in beide Richtungen, .ps1-BOM 2. `pytest` — die Szenario-Suite ⚠ NOTAUSGANG: `git commit --no-verify` ueberspringt den Hook. Das ist Absicht — eine Pruefung, die einen dringenden Fix blockiert, wird sonst dauerhaft deinstalliert. Wer ihn nutzt, sollte die Pruefung danach nachholen. ⚠ BEWUSST OHNE das `pre-commit`-Framework: dessen Nutzen ist die Verwaltung von Werkzeug-Versionen ueber mehrere Entwickler und Maschinen hinweg. Hier gibt es eine Maschine und einen Entwickler; ein eigenes, versioniertes Skript ist transparenter und braucht keine isolierten Zusatz-Umgebungen (die dann eine ANDERE ruff-Version haetten als die Kommandozeile — genau die Art Divergenz, die dieses Projekt schon oft genug hatte). Aufruf: python tools/pre_commit.py # Pruefung von Hand python tools/pre_commit.py --install # als Git-Hook einrichten """ import subprocess import sys import time from pathlib import Path WURZEL = Path(__file__).resolve().parents[1] PY = sys.executable def lauf(titel: str, argv: list[str]) -> bool: t0 = time.time() r = subprocess.run(argv, cwd=WURZEL, capture_output=True, text=True) dauer = time.time() - t0 if r.returncode == 0: print(f" ✅ {titel:<28} {dauer:.1f}s") return True print(f" ❌ {titel:<28} {dauer:.1f}s") # Nur die letzten Zeilen — der Hook soll die Ursache zeigen, nicht fluten. for z in (r.stdout + r.stderr).strip().splitlines()[-25:]: print(f" {z}") return False def installieren() -> int: hooks = WURZEL / ".git" / "hooks" if not hooks.is_dir(): print(" Kein .git/hooks-Verzeichnis gefunden."); return 1 ziel = hooks / "pre-commit" # ⚠ Git fuer Windows fuehrt Hooks ueber `sh` aus, nicht ueber cmd — deshalb # ein sh-Shim mit Vorwaertsschraegstrichen. ziel.write_text( "#!/bin/sh\n" "# Erzeugt von tools/pre_commit.py --install\n" f'exec "{Path(PY).as_posix()}" -X utf8 ' f'"{(WURZEL / "tools" / "pre_commit.py").as_posix()}"\n', encoding="utf-8", newline="\n") print(f" Hook installiert: {ziel}") print(" Ueberspringen im Notfall: git commit --no-verify") return 0 def main() -> int: if "--install" in sys.argv: return installieren() print("── Pre-Commit ─────────────────────────────────────") ok = True ok &= lauf("Syntax · Linter · Scanner", [PY, "-X", "utf8", str(WURZEL / "tools" / "check_nfalle.py")]) ok &= lauf("Szenario-Tests (pytest)", [PY, "-X", "utf8", "-m", "pytest", "-q"]) if ok: print("── alles gruen ────────────────────────────────────") return 0 print("── COMMIT GESTOPPT ────────────────────────────────") print(" Beheben — oder bewusst uebergehen: git commit --no-verify") return 1 if __name__ == "__main__": sys.exit(main())