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.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user