From 4cbb1f1db550132eaa631a4bccb8b8503b7c6d9b Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Fri, 7 Aug 2026 06:28:30 +0200 Subject: [PATCH] tools/deploy.py: Stufe 5 der Pipeline ist jetzt ein pruefbares Skript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ersetzt das von Hand zusammengesetzte "killen, starten, warten, nachsehen" — und macht die dokumentierte Schwachstelle der Pipeline pruefbar. Fuenf Schritte, Rueckgabecode 0 nur wenn alle durchlaufen (verkettbar): 1. Prozess GEZIELT AM PORT beenden — nicht per *server.py*-Muster wie restart_server.bat, das trifft auch das HL-Dashboard auf 8001 (real: es wurde bei jedem MT5-Neustart still mit-erschlagen und riss die Luecken in die Lead-Lag- und Order-Flow-Datensammlung). 2. starten und warten, bis /api/snapshot WIRKLICH antwortet. 3. genau EINE Instanz je Port. 4. --feld pruefen. 5. Log AB DER STARTPOSITION auf ERROR/Traceback. Schritt 4 ist der Kern: restart_server.bat hat zweimal still nicht neu gestartet, und weil statische Dateien je Request frisch von der Platte gelesen werden, belegt eine korrekt ausgelieferte app.js?v=N gar nichts ueber den geladenen Python-Code. Ein neues Snapshot-Feld ist der einzige belastbare Beweis — genau daran fiel der Fehler am 02.08. auf (hl_live.basis_stale). Ohne --feld sagt das Skript ausdruecklich, dass der Beweis fehlt. Beide Richtungen geprueft: --feld rec_track -> "Python-Code ist neu", Exit 0 --feld dieses_feld_gibt_es_nicht -> "FEHLT - der alte Code laeuft weiter!", Exit 1 Eine Pruefung, die nicht scheitern kann, waere wertlos. Aufrufe: python tools/deploy.py --feld python tools/deploy.py --nur-pruefen python tools/deploy.py --hl Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 24 +++++++ tools/deploy.py | 173 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 tools/deploy.py diff --git a/CLAUDE.md b/CLAUDE.md index face171..51a4409 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -259,6 +259,30 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche: Anteil, WR-Verfall, PF<1, Einzelverlust). **Wöchentlich** laufen lassen (B4/B5). - **Vor „fertig":** immer `python -m py_compile `; JS grob via Klammern- Balance prüfen (kein node im Env). +- **✅ STUFE 5 DER PIPELINE IST JETZT EIN SKRIPT (`tools/deploy.py`, 2026-08-07).** + Ersetzt das von Hand zusammengesetzte „killen, starten, warten, nachsehen" und + macht die dokumentierte Schwachstelle prüfbar. + ``` + python tools/deploy.py --feld # Neustart + Beweis + python tools/deploy.py --nur-pruefen # nichts anfassen, nur Zustand + python tools/deploy.py --hl # HL-Dashboard (8001) mitnehmen + ``` + Fünf Schritte, **Rückgabecode 0 nur wenn alle durchlaufen** (verkettbar): + **(1)** Prozess **gezielt am Port** beenden — ⚠ NICHT per `*server.py*`-Muster + wie `restart_server.bat`, das trifft auch das HL-Dashboard auf 8001 (real: es + wurde bei jedem MT5-Neustart still mit-erschlagen und riss die Lücken in die + Datensammlung). **(2)** starten und warten, bis `/api/snapshot` **wirklich** + antwortet. **(3)** **genau EINE** Instanz je Port. **(4)** `--feld` prüfen. + **(5)** Log **ab der Startposition** auf ERROR/Traceback. + ⚠⚠ **Schritt 4 ist der Kern.** `restart_server.bat` hat zweimal still nicht neu + gestartet, und weil statische Dateien je Request frisch von der Platte gelesen + werden, **belegt eine korrekt ausgelieferte `app.js?v=N` GAR NICHTS** über den + geladenen Python-Code. Ein neues Snapshot-Feld ist der einzige belastbare + Beweis — genau daran fiel der Fehler 2026-08-02 auf (`hl_live.basis_stale`). + Ohne `--feld` sagt das Skript das ausdrücklich dazu. + ✅ **Beide Richtungen geprüft**: mit `--feld rec_track` → „Python-Code ist neu", + Exit 0; mit einem erfundenen Feld → „FEHLT — der alte Code läuft weiter!", + **Exit 1**. Eine Prüfung, die nicht scheitern kann, wäre wertlos. - **⚠⚠ DER ENGPASS DES PROJEKTS WAR NIE DIE MATHEMATIK — er war eine quadratische Schleife (Profil 2026-08-07, ~9 min → 8,9 s).** Anlass war die Frage nach numpy. Statt zu vermuten wurde profiliert, und das Ergebnis war ein diff --git a/tools/deploy.py b/tools/deploy.py new file mode 100644 index 0000000..eb15706 --- /dev/null +++ b/tools/deploy.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""Stufe 5 der Pipeline: Server zuverlässig neu starten UND das Ergebnis prüfen. + +⚠⚠ WARUM ES DIESES SKRIPT GIBT. `restart_server.bat` hat **zweimal still nicht +neu gestartet** (auch als `Start-Process cmd /c … -WindowStyle Minimized`) — der +alte Prozess lief weiter, und weil statische Dateien bei jedem Request frisch von +der Platte gelesen werden, **belegt eine korrekt ausgelieferte `app.js?v=N` GAR +NICHTS** über den geladenen Python-Code. Der Fehler fiel damals nur auf, weil ein +NEUES Snapshot-Feld fehlte (`hl_live.basis_stale`). + +Dieses Skript macht daraus einen prüfbaren Ablauf: + 1. Prozess auf dem Port gezielt beenden — **nicht** per `*server.py*`-Muster, + das trifft auch das Hyperliquid-Dashboard auf 8001 (real passiert: der + HL-Server wurde bei jedem MT5-Neustart still mit-erschlagen und riss die + Lücken in die Datensammlung). + 2. Neu starten und warten, bis `/api/snapshot` wirklich antwortet. + 3. **Genau EINE** Instanz je Port verifizieren. + 4. Optional `--feld ` prüfen: ist dieses Snapshot-Feld da, ist der + PYTHON-Code nachweislich neu. Das ist der einzige belastbare Beweis. + 5. Das Log seit dem Start auf Fehler durchsehen. + +Rückgabecode 0 nur, wenn ALLE Schritte durchlaufen — so lässt sich das Skript +verketten, statt das Ergebnis von Hand zu lesen. + +Aufruf: + python tools/deploy.py # Oil-Server (8000) neu starten + python tools/deploy.py --feld rec_track # zusätzlich Code-Neuheit belegen + python tools/deploy.py --hl # HL-Dashboard (8001) mitnehmen + python tools/deploy.py --nur-pruefen # nichts anfassen, nur Zustand +""" +import argparse +import json +import subprocess +import sys +import time +import urllib.request +from pathlib import Path + +WURZEL = Path(__file__).resolve().parents[1] +PY = r"C:\Users\ah\AppData\Local\Programs\Python\Python312\python.exe" +HL = WURZEL.parent / "HyperLiquid-WTI Trader" +LOG = WURZEL / "oil_widget.log" +PORT, PORT_HL = 8000, 8001 +START_TIMEOUT_S = 120 + + +def ps(cmd: str) -> str: + """PowerShell — für die Prozess-/Port-Abfragen der zuverlässigste Weg hier.""" + r = subprocess.run(["powershell", "-NoProfile", "-Command", cmd], + capture_output=True, text=True, timeout=60) + return (r.stdout or "").strip() + + +def pids(port: int) -> list[int]: + out = ps(f"(Get-NetTCPConnection -LocalPort {port} -State Listen " + f"-ErrorAction SilentlyContinue | Select-Object -Expand " + f"OwningProcess -Unique) -join ','") + return [int(x) for x in out.split(",") if x.strip().isdigit()] + + +def snapshot(timeout: float = 5.0) -> dict | None: + try: + with urllib.request.urlopen( + f"http://127.0.0.1:{PORT}/api/snapshot", timeout=timeout) as r: + return json.loads(r.read().decode()) + except Exception: + return None + + +def schritt(nr: int, text: str): + print(f" [{nr}] {text}") + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--feld", help="Snapshot-Feld, das den neuen Code belegt") + ap.add_argument("--hl", action="store_true", help="HL-Dashboard (8001) mit") + ap.add_argument("--nur-pruefen", action="store_true") + a = ap.parse_args() + + print("=" * 74) + print(" DEPLOY — Stufe 5 (Aktivierung) + Stufe 6 (Verifikation)") + print("=" * 74) + fehler: list[str] = [] + + if a.nur_pruefen: + for p in (PORT, PORT_HL): + print(f" Port {p}: {pids(p) or 'nichts'}") + s = snapshot() + print(f" Snapshot: {'antwortet' if s else 'KEINE Antwort'}") + return 0 if s else 1 + + # ── 1) gezielt beenden ─────────────────────────────────────────────── + alt = pids(PORT) + schritt(1, f"Port {PORT}: {alt or 'nichts'} → beenden") + for pid in alt: + ps(f"Stop-Process -Id {pid} -Force -ErrorAction SilentlyContinue") + if alt: + time.sleep(3) + if a.hl and HL.exists(): + for pid in pids(PORT_HL): + ps(f"Stop-Process -Id {pid} -Force -ErrorAction SilentlyContinue") + time.sleep(2) + ps(f"Start-Process -FilePath '{PY}' -ArgumentList '-X','utf8','server.py' " + f"-WorkingDirectory '{HL}' -WindowStyle Hidden") + schritt(1, f"HL-Dashboard ({PORT_HL}) neu gestartet") + + log_ab = LOG.stat().st_size if LOG.exists() else 0 + + # ── 2) starten und auf echte Antwort warten ────────────────────────── + schritt(2, "Server starten …") + ps(f"Start-Process -FilePath '{PY}' -ArgumentList '-X','utf8','server.py' " + f"-WorkingDirectory '{WURZEL}' -WindowStyle Minimized") + t0 = time.time() + snap = None + while time.time() - t0 < START_TIMEOUT_S: + time.sleep(3) + snap = snapshot() + if snap: + break + if not snap: + print(f" ❌ /api/snapshot antwortet nach {START_TIMEOUT_S} s nicht.") + return 1 + schritt(2, f"läuft nach {time.time() - t0:.0f} s") + + # ── 3) genau EINE Instanz ──────────────────────────────────────────── + for p, noetig in ((PORT, True), (PORT_HL, a.hl)): + ids = pids(p) + if not noetig and not ids: + continue + if len(ids) == 1: + schritt(3, f"Port {p}: genau eine Instanz (PID {ids[0]}) ✅") + else: + fehler.append(f"Port {p}: {len(ids)} Instanzen {ids}") + schritt(3, f"Port {p}: ⚠ {len(ids)} Instanzen {ids}") + + # ── 4) beweist ein neues Feld, dass der PYTHON-Code neu ist? ───────── + if a.feld: + if a.feld in snap: + schritt(4, f"Feld `{a.feld}` vorhanden → Python-Code ist neu ✅") + else: + fehler.append(f"Feld `{a.feld}` fehlt im Snapshot") + schritt(4, f"⚠ Feld `{a.feld}` FEHLT — der alte Code läuft weiter!") + else: + schritt(4, "kein --feld angegeben ⚠ ohne das ist NICHT belegt, dass der " + "Python-Code neu ist (statische Dateien beweisen nichts)") + + # ── 5) Log seit dem Start ──────────────────────────────────────────── + treffer = [] + if LOG.exists(): + with LOG.open(encoding="utf-8", errors="replace") as fh: + fh.seek(log_ab) + for z in fh: + if any(w in z for w in ("ERROR", "Traceback", "CRITICAL")): + treffer.append(z.rstrip()[:130]) + if treffer: + fehler.append(f"{len(treffer)} Fehlerzeile(n) im Log") + schritt(5, f"⚠ {len(treffer)} Fehlerzeile(n) seit dem Start:") + for z in treffer[:5]: + print(f" {z}") + else: + schritt(5, "Log seit dem Start ohne ERROR/Traceback ✅") + + print("=" * 74) + if fehler: + print(" ERGEBNIS: ❌ " + " · ".join(fehler)) + return 1 + print(" ERGEBNIS: ✅ neu gestartet und verifiziert") + return 0 + + +if __name__ == "__main__": + sys.exit(main())