homelab: Update-Weg je App (Knopf nur, wo das Community-Script die App wirklich aktualisiert), Version von PVE Scripts Local aus der App selbst
Nachgelesen in ct/<app>.sh: AdGuard und PVE Scripts Local verweisen auf ihren eingebauten Updater, Gitea/NetBird/NPMplus aktualisieren wirklich, PBS ueber die Pakete. /root/.proxmoxve-local stand noch auf 0.5.8, die App selbst schon auf der neuesten Veroeffentlichung (v1.2.1, untagged). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
56df849455
commit
528198de60
@@ -41,4 +41,5 @@ def neueste_version(repo: str) -> dict | None:
|
||||
return None
|
||||
tag, name = str(daten["tag_name"]), str(daten.get("name") or "")
|
||||
roh = tag if _VERSION.match(tag) or not _VERSION.match(name) else name
|
||||
return {"tag": roh, "version": roh.lstrip("vV"), "datum": str(daten.get("published_at") or "")[:10] or None}
|
||||
return {"tag": roh, "tag_roh": tag, "version": roh.lstrip("vV"),
|
||||
"datum": str(daten.get("published_at") or "")[:10] or None}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
"""Was in welchem Container steckt (Community-Scripts-Kennung aus /usr/bin/update im Gast).
|
||||
|
||||
Stand der Bestandsaufnahme vom 24.09.2026: sechs Container, alle mit dem Etikett community-script.
|
||||
|
||||
Wie eine App aktualisiert wird, ist je Community-Script verschieden (nachgelesen am 24.09. in ct/<app>.sh):
|
||||
skript `update` im Gast spielt die App wirklich neu ein (Gitea: neues Programm von GitHub,
|
||||
NetBird: apt aus dem NetBird-Repository, NPMplus: neues Docker-Image)
|
||||
eigene-oberflaeche das Skript verweist auf den eingebauten Updater der App (AdGuard, PVE Scripts Local)
|
||||
pakete die App kommt als Paket; das Update der Pakete im Gast ist ihr Update (PBS)
|
||||
Nur beim Weg „skript“ bietet die Übersicht „Jetzt updaten“ für die App an.
|
||||
Neue Container mit dem Etikett erscheinen von selbst; fehlt ihre Kennung hier, zeigt die Übersicht
|
||||
sie ohne Versionsvergleich und ohne Webprüfung — nachtragen genügt.
|
||||
"""
|
||||
@@ -17,20 +24,22 @@ class App:
|
||||
port: int | None = None # Weboberfläche; None = keine (z. B. NetBird-Client)
|
||||
https: bool = False
|
||||
pfad: str = "/"
|
||||
weg: str = "skript" # „skript“, „eigene-oberflaeche“ oder „pakete“ (siehe oben)
|
||||
|
||||
|
||||
KATALOG: dict[str, App] = {a.kennung: a for a in (
|
||||
App("adguard", "AdGuard Home", "AdguardTeam/AdGuardHome", port=8080),
|
||||
App("adguard", "AdGuard Home", "AdguardTeam/AdGuardHome", port=8080, weg="eigene-oberflaeche"),
|
||||
App("npmplus", "NPMplus", "ZoeyVid/NPMplus", vergleich="datum", port=81, https=True),
|
||||
App("netbird", "NetBird", "netbirdio/netbird"),
|
||||
App("pve-scripts-local", "PVE Scripts Local", "community-scripts/ProxmoxVE-Local", port=3000),
|
||||
App("pve-scripts-local", "PVE Scripts Local", "community-scripts/ProxmoxVE-Local", port=3000,
|
||||
weg="eigene-oberflaeche"),
|
||||
App("gitea", "Gitea", "go-gitea/gitea", port=3000),
|
||||
App("proxmox-backup-server", "Proxmox Backup Server", port=8007, https=True),
|
||||
App("proxmox-backup-server", "Proxmox Backup Server", port=8007, https=True, weg="pakete"),
|
||||
)}
|
||||
|
||||
# Gäste, die keine Community-Scripts sind, erkennt man am Namen.
|
||||
NACH_NAME: dict[str, App] = {
|
||||
"arcane": App("arcane", "Arcane (Docker)", port=3552),
|
||||
"arcane": App("arcane", "Arcane (Docker)", port=3552, weg="eigene-oberflaeche"),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -83,6 +83,13 @@ def _app_baustein(gast: dict, app: apps.App, zid: str) -> BausteinStand:
|
||||
stand.zustand, stand.grund = "unbekannt", "Die neueste Version ist gerade nicht abrufbar (GitHub)."
|
||||
return stand
|
||||
stand.verfuegbar = neu["version"]
|
||||
if installiert and installiert.startswith("untagged-"):
|
||||
# Manche Projekte veröffentlichen ohne Versions-Tag; die App kennt dann nur dieses Tag (PVE Scripts Local).
|
||||
if installiert == neu.get("tag_roh"):
|
||||
stand.installiert = neu["version"]
|
||||
return stand
|
||||
stand.zustand, stand.grund = "unbekannt", "Die installierte Fassung lässt sich keiner Version zuordnen."
|
||||
return stand
|
||||
if app.vergleich == "datum":
|
||||
# Docker-Image ohne Versionsnummer: neuer, wenn die Veröffentlichung jünger ist als das Image.
|
||||
bild = re.search(r"\d{4}-\d{2}-\d{2}", installiert or "")
|
||||
@@ -91,6 +98,9 @@ def _app_baustein(gast: dict, app: apps.App, zid: str) -> BausteinStand:
|
||||
ist_neuer = neuer(neu["version"], installiert) if installiert else None
|
||||
if ist_neuer is None:
|
||||
stand.zustand, stand.grund = "unbekannt", "Die installierte Version ließ sich nicht lesen."
|
||||
elif ist_neuer and app.weg != "skript":
|
||||
stand.zustand, stand.kurz = "neu", f"{installiert} → {neu['version']}"
|
||||
stand.grund = f"{app.name} aktualisiert sich über die eigene Oberfläche; das Community-Script tut es nicht."
|
||||
elif ist_neuer:
|
||||
stand.zustand, stand.kurz = "neu", f"{installiert} → {neu['version']}"
|
||||
rueckweg = ("Vorher wird ein Snapshot angelegt; ist die Prüfung danach rot, geht es von selbst zurück."
|
||||
|
||||
@@ -244,6 +244,9 @@ def starten(ziel_id: str, baustein: str) -> dict:
|
||||
return {"ok": False, "detail": "Dieses Gerät ist nicht freigegeben (Etikett watcher fehlt)."}
|
||||
app = apps.app_fuer((gast.get("app") or {}).get("kennung"), gast.get("name"))
|
||||
name = app.name if app else str(gast.get("name"))
|
||||
if baustein == "app" and (not app or app.weg != "skript"):
|
||||
return {"ok": False, "detail": f"{name} aktualisiert sich nicht über das Community-Script "
|
||||
"(eigene Oberfläche oder Pakete)."}
|
||||
lauf = _neuer_lauf(ziel_id, baustein, name)
|
||||
ziel = _host_lauf if gast is None else (lambda lf: _gast_lauf(lf, gast))
|
||||
threading.Thread(target=ziel, args=(lauf,), name=f"homelab-{ziel_id}", daemon=True).start()
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@
|
||||
"ip": "192.168.178.149",
|
||||
"app": {
|
||||
"kennung": "pve-scripts-local",
|
||||
"version": "0.5.8"
|
||||
"version": "untagged-80028680f0b29a379726"
|
||||
},
|
||||
"os_updates": {
|
||||
"anzahl": 0,
|
||||
|
||||
@@ -27,9 +27,10 @@ def daten(tmp_path, monkeypatch):
|
||||
# Keine Netzabfragen: neueste Versionen und Weboberflächen sind fest.
|
||||
neueste = {"AdguardTeam/AdGuardHome": "0.107.79", "go-gitea/gitea": "1.27.3", "netbirdio/netbird": "0.79.0",
|
||||
"community-scripts/ProxmoxVE-Local": "1.2.1"}
|
||||
roh = {"community-scripts/ProxmoxVE-Local": "untagged-80028680f0b29a379726"}
|
||||
monkeypatch.setattr(inventar, "neueste_version", lambda repo: {
|
||||
"tag": neueste.get(repo, "2026-07-24-r1"), "version": neueste.get(repo, "2026-07-24-r1"),
|
||||
"datum": "2026-07-24"})
|
||||
"tag": neueste.get(repo, "2026-07-24-r1"), "tag_roh": roh.get(repo, neueste.get(repo, "2026-07-24-r1")),
|
||||
"version": neueste.get(repo, "2026-07-24-r1"), "datum": "2026-07-24"})
|
||||
monkeypatch.setattr(inventar, "erreichbar", lambda url: True)
|
||||
monkeypatch.setattr(inventar.arcane, "app_version",
|
||||
lambda url: {"installiert": "2.12.0", "neu": "2.13.1", "update": True})
|
||||
@@ -106,7 +107,9 @@ def test_ziele_aus_dem_echten_bericht(daten):
|
||||
assert (pbs["os"]["zustand"], pbs["os"]["kurz"]) == ("neu", "58 Pakete")
|
||||
assert "Bind-Mount" in z["ct-105"]["rueckweg"]
|
||||
|
||||
assert {b["id"]: b for b in z["ct-103"]["bausteine"]}["app"]["kurz"] == "0.5.8 → 1.2.1"
|
||||
# PVE Scripts Local steht auf der neuesten (untagged) Veröffentlichung: aktuell, angezeigt als 1.2.1.
|
||||
pvesl = {b["id"]: b for b in z["ct-103"]["bausteine"]}["app"]
|
||||
assert (pvesl["zustand"], pvesl["installiert"], pvesl["aktion"]) == ("aktuell", "1.2.1", None)
|
||||
assert {b["id"]: b for b in z["ct-101"]["bausteine"]}["app"]["zustand"] == "aktuell" # Image jünger
|
||||
# NetBird hat keine Weboberfläche: erreichbar heißt hier „läuft“.
|
||||
assert z["ct-102"]["erreichbar"] is True
|
||||
@@ -334,3 +337,14 @@ def test_hinweise_des_homelab_waechters(daten, monkeypatch):
|
||||
encoding="utf-8")
|
||||
antwort = _client().get("/api/homelab/hinweise").json()
|
||||
assert [h["titel"] for h in antwort["hinweise"]] == ["Gitea antwortet nicht"]
|
||||
|
||||
|
||||
def test_apps_mit_eigenem_updater_bekommen_keinen_knopf(daten, monkeypatch):
|
||||
"""AdGuard und PVE Scripts Local aktualisiert das Community-Script nicht (ct/<app>.sh, 24.09.2026)."""
|
||||
bericht = json.loads(json.dumps(BERICHT))
|
||||
next(g for g in bericht["gaeste"] if g["vmid"] == 100)["app"]["version"] = "0.107.70"
|
||||
kanal.bericht_speichern(bericht)
|
||||
adguard = {b["id"]: b for b in {z["id"]: z for z in inventar.ziele()["ziele"]}["ct-100"]["bausteine"]}["app"]
|
||||
assert adguard["zustand"] == "neu" and adguard["aktion"] is None and "eigene Oberfläche" in adguard["grund"]
|
||||
antwort = updates.starten("ct-103", "app")
|
||||
assert antwort["ok"] is False and "Community-Script" in antwort["detail"]
|
||||
|
||||
@@ -46,7 +46,8 @@ def test_github_antworten_werden_gemerkt_fehler_nicht(monkeypatch):
|
||||
monkeypatch.setattr(github.httpx, "get", lambda url, **kw: aufrufe.append(url) or antworten.pop(0))
|
||||
with pytest.raises(httpx.HTTPStatusError):
|
||||
github.github_json("repos/a/b/releases/latest")
|
||||
assert github.neueste_version("a/b") == {"tag": "v258", "version": "258", "datum": "2026-09-20"}
|
||||
assert github.neueste_version("a/b") == {"tag": "v258", "tag_roh": "v258", "version": "258",
|
||||
"datum": "2026-09-20"}
|
||||
assert github.github_json("repos/a/b/releases/latest")["tag_name"] == "v258"
|
||||
assert len(aufrufe) == 2
|
||||
assert maintenance._github_json is github.github_json
|
||||
|
||||
@@ -50,7 +50,9 @@ VERSION_PROBEN = {
|
||||
"proxmox-backup-server": "dpkg-query -W proxmox-backup-server",
|
||||
"npmplus": "docker image inspect zoeyvid/npmplus:latest --format '{{.Created}}'",
|
||||
}
|
||||
VERSIONSDATEIEN = {"pve-scripts-local": "/root/.proxmoxve-local"}
|
||||
# PVE Scripts Local aktualisiert sich über den eingebauten Updater; /root/.proxmoxve-local bleibt dabei auf dem
|
||||
# Stand der Erstinstallation stehen (24.09.2026: „0.5.8“, tatsächlich die neueste Veröffentlichung).
|
||||
VERSIONSDATEIEN = {"pve-scripts-local": "/opt/ProxmoxVE-Local/VERSION"}
|
||||
|
||||
log = logging.getLogger("ausfuehrer")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user