"""Wartezeit nach einer Änderung am Update-Skript (24.09.2026). „Jetzt updaten“ lässt im Gast `update` laufen. Das lädt ct/.sh ungepinnt von GitHub (community-scripts/ProxmoxVE, Zweig main) und führt es als root aus. Eine Änderung, die erst ein paar Stunden alt ist, hat noch kaum jemand im Betrieb gesehen. Deshalb gilt für Apps mit dem Weg „skript“: Ist die letzte Änderung an ct/.sh jünger als MC_HOMELAB_KARENZ_H Stunden (Standard 48), bleibt der Baustein „neu“, aber ohne Knopf, und updates.starten() lehnt ab — beide mit demselben Satz. Wann das Skript zuletzt geändert wurde, sagt die GitHub-API (kern/github.py, 15 Minuten gemerkt). Lässt sich das nicht abfragen, wird nicht blockiert; die Rückfrage vor dem Update sagt es dann (NICHT_GEPRUEFT). """ import os import re import time from datetime import datetime from kern.github import github_json from kern.zeit import LOCAL_TZ REPO = "community-scripts/ProxmoxVE" STANDARD_H = 48.0 NICHT_GEPRUEFT = "Ob das Skript kürzlich geändert wurde, ließ sich nicht prüfen." _KENNUNG = re.compile(r"^[a-z0-9-]+$") def karenz_s() -> float: """Die Wartezeit in Sekunden (MC_HOMELAB_KARENZ_H; 0 schaltet sie ab).""" try: stunden = float(os.environ.get("MC_HOMELAB_KARENZ_H", "") or STANDARD_H) except ValueError: stunden = STANDARD_H return max(0.0, stunden) * 3600 def skript_geaendert(kennung: str) -> float: """Wann ct/.sh auf main zuletzt geändert wurde (Unix-Sekunden). Wirft, wenn es sich nicht sagen lässt.""" if not _KENNUNG.match(kennung or ""): raise ValueError(f"ungültige Kennung {kennung!r}") daten = github_json(f"repos/{REPO}/commits?path=ct/{kennung}.sh&sha=main&per_page=1") # Das Datum, an dem die Änderung auf main landete (committer, nicht author: ein Pull-Request kann älter sein). datum = str(daten[0]["commit"]["committer"]["date"]) return datetime.fromisoformat(datum.replace("Z", "+00:00")).timestamp() def sperrtext(geaendert: float, frei_ab: float) -> str: a, b = datetime.fromtimestamp(geaendert, LOCAL_TZ), datetime.fromtimestamp(frei_ab, LOCAL_TZ) return f"Das Update-Skript wurde am {a:%d.%m.} geändert; zur Sicherheit erst ab {b:%d.%m. %H:%M}." def pruefen(kennung: str, jetzt: float | None = None) -> dict: """{"gesperrt": Satz oder None, "geprueft": bool}. Gesperrt, solange die letzte Änderung am Skript jünger ist als die Wartezeit. Scheitert die Abfrage: nicht gesperrt, aber geprueft=False.""" try: geaendert = skript_geaendert(kennung) except Exception: return {"gesperrt": None, "geprueft": False} frei_ab = geaendert + karenz_s() if (time.time() if jetzt is None else jetzt) >= frei_ab: return {"gesperrt": None, "geprueft": True} return {"gesperrt": sperrtext(geaendert, frei_ab), "geprueft": True}