Files
AH-Oil-Trader/tools/pre_commit.py
T
Axel Hocks a35a0e7006 Pipeline-Stufe 1 automatisiert: requirements, pre-commit-Hook, ruff
Nach Web-Recherche zum 2026er-Python-Standard umgesetzt, aber gefiltert: von
uv/Ruff/pyproject/CI passt hier nur, was zu einer Einzelmaschine mit
GUI-gebundenem MT5 und Live-Geld passt.

(1) requirements.txt (Laufzeit) + requirements-dev.txt (Werkzeuge/Messung),
    Versionen gepinnt. Bis dahin gab es GAR KEINE Liste — nach einem
    Maschinenausfall waere die Rekonstruktion Raten gewesen. Per AST ueber
    core/, server.py, tools/ ermittelt statt geraten.

(2) tools/pre_commit.py faehrt vor jedem Commit check_nfalle + pytest (~5 s).
    Einrichten: python tools/pre_commit.py --install. Notausgang
    'git commit --no-verify' ist Absicht.
    Bewusst OHNE das pre-commit-Framework: dessen Nutzen ist
    Werkzeug-Versionierung ueber mehrere Maschinen. Hier gibt es eine, und es
    wuerde eine ZWEITE ruff-Version in einer isolierten Umgebung installieren —
    genau die Divergenz 'Pruefumgebung != Laufumgebung', die heute schon einmal
    zugeschlagen hat.

(3) Ruff statt pyflakes (--select F821), ganzer Scan in 2,1 s. KEIN
    ruff format — das wuerde 161 Dateien umformatieren, darunter alle
    Backtests, und die Migrations-Regel verletzen.

⚠ VIERTER Fall der Klasse 'Erfolg melden, wo nichts geprueft wurde', und nur
durch den Test aufgefallen: --select F821,E999 laesst ruff 0.16 komplett
abbrechen (E999 wurde entfernt, Exit 2); die Meldung geht nach stderr, und weil
ich den Rueckgabecode nicht pruefte, meldete die Stufe 'keine undefinierten
Namen' — ohne irgendetwas geprueft zu haben. Ein absichtlich eingebauter
F821-Fehler kam glatt durch den Hook. Behoben: nur F821, und Exit 2 loest jetzt
den pyflakes-Rueckfall aus.

Beide Richtungen verifiziert: mit Fehler stoppt der Hook den Commit, ohne ist
er gruen. Der Test-Commit wurde zurueckgenommen (war nur lokal, ahead 1).

Nicht umgesetzt: Gitea Actions (Runner auf derselben Maschine wie der Live-Bot,
kein Mehrwert bei 5 s Pruefzeit), Container/Blue-Green (MT5 braucht eine
Desktop-Sitzung, EINE Broker-Verbindung), mypy/ty (kaum Annotationen).

Struktureller Fund: core/engine.py importiert backtest_breakout_squeeze — ein
Backtest ist Teil des LIVE-Abhaengigkeitsgraphen.
2026-08-07 07:08:02 +02:00

90 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 (~34 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())