tools/deploy.py: Stufe 5 der Pipeline ist jetzt ein pruefbares Skript
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 <name> 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 <snapshot_feld>
python tools/deploy.py --nur-pruefen
python tools/deploy.py --hl
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
cddcefa463
commit
4cbb1f1db5
@@ -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 <datei>`; 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 <snapshot_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
|
||||
|
||||
+173
@@ -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 <name>` 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())
|
||||
Reference in New Issue
Block a user