6ebe95c13e
gates / gates (push) Successful in 41s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
89 lines
3.6 KiB
Python
89 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Part C step 2, SETUP on 9202: four apps installed through the product, each at the ladder's first `from`,
|
|
then the drill catalog put back at the head — so wishlist is 1 step behind, navidrome 2, romm 3, and vikunja 1
|
|
with a FAILING step (its head .felhom.yml probe points at a closed port AFTER the install, so the install's
|
|
applied record keeps the good probe and the undo can judge the old version correctly).
|
|
The drill also receives the live catalog's step .felhom.yml files (catalog cf7cf84) the reset predates."""
|
|
import json, os, re, shutil, sys, time
|
|
sys.path.insert(0, ".")
|
|
import walk as w
|
|
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
|
|
import ladder
|
|
|
|
LIVE = "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/templates"
|
|
APPS = {"wishlist": "c-wish", "navidrome": "c-navi", "romm": "c-romm", "vikunja": "c-vik"}
|
|
T = lambda a: f"{w.DRILL}/templates/{a}"
|
|
out = {"controller": w.guest("cat /etc/felhom-controller-image").strip(), "apps": {}}
|
|
w.login()
|
|
|
|
|
|
def drill(edit, msg):
|
|
import fcntl
|
|
with open(f"{w.SC}/drill.lock", "w") as lk:
|
|
fcntl.flock(lk, fcntl.LOCK_EX)
|
|
w.sh(["git", "-C", w.DRILL, "pull", "-q", "--rebase", "origin", "main"], timeout=120)
|
|
edit()
|
|
w.sh(["git", "-C", w.DRILL, "add", "-A", "templates"])
|
|
w.sh(["git", "-C", w.DRILL, "commit", "-qm", "NIGHT-C " + msg])
|
|
w.sh(["git", "-C", w.DRILL, "push", "-q", "origin", "main"], timeout=120)
|
|
w.say("drill: " + msg)
|
|
|
|
|
|
HEAD = {a: open(f"{T(a)}/docker-compose.yml").read() for a in APPS}
|
|
FY = {a: open(f"{T(a)}/.felhom.yml").read() for a in APPS}
|
|
|
|
|
|
def old_compose(a):
|
|
E, _, _ = ladder.parse(FY[a])
|
|
frm, head = E[0]["from"], ladder.images_in(HEAD[a])
|
|
c = HEAD[a]
|
|
for svc, ref in head.items():
|
|
if frm.get(svc) and frm[svc] != ref:
|
|
c = c.replace(f"image: {ref}", f"image: {frm[svc]}", 1)
|
|
assert ladder.images_in(c) == frm, (a, ladder.images_in(c), frm)
|
|
return c, len(E)
|
|
|
|
|
|
def to_old():
|
|
for a in APPS:
|
|
c, n = old_compose(a)
|
|
open(f"{T(a)}/docker-compose.yml", "w").write(c)
|
|
out["apps"][a] = {"ladder_len": n, "installed_at": ladder.images_in(c)}
|
|
for a in ("navidrome", "romm"):
|
|
for f in os.listdir(f"{LIVE}/{a}/steps"):
|
|
if f.endswith(".felhom.yml"):
|
|
shutil.copy(f"{LIVE}/{a}/steps/{f}", f"{T(a)}/steps/{f}")
|
|
|
|
|
|
drill(to_old, "wishlist/navidrome/romm/vikunja at the ladder's first from (+ step .felhom.yml files from live cf7cf84)")
|
|
w.sync_rescan()
|
|
for a, sub in APPS.items():
|
|
t0 = time.time()
|
|
ok = w.deploy(a, sub)
|
|
st = w.stack(a)
|
|
out["apps"][a]["deploy_ok"] = ok
|
|
out["apps"][a]["deploy_s"] = round(time.time() - t0)
|
|
out["apps"][a]["pinned"] = (st.get("app_config") or {}).get("pinned_images")
|
|
w.say(f"{a}: deployed={ok} in {out['apps'][a]['deploy_s']} s pinned={out['apps'][a]['pinned']}")
|
|
|
|
|
|
def to_head():
|
|
for a in APPS:
|
|
open(f"{T(a)}/docker-compose.yml", "w").write(HEAD[a])
|
|
f = re.sub(r"(healthcheck:\n\s+checks:\n\s+- type: api\n\s+port: )3456\b", r"\g<1>8999", FY["vikunja"], count=1)
|
|
assert f != FY["vikunja"]
|
|
open(f"{T('vikunja')}/.felhom.yml", "w").write(f)
|
|
|
|
|
|
drill(to_head, "heads back; vikunja's head step probes port 8999 (the failing step)")
|
|
w.sync_rescan()
|
|
time.sleep(5)
|
|
w.sync_rescan()
|
|
for a in APPS:
|
|
st = w.stack(a)
|
|
out["apps"][a]["steps_left"] = st.get("ladder_steps_left")
|
|
out["apps"][a]["badges"] = w.badges(a)
|
|
w.say(f"{a}: steps_left={st.get('ladder_steps_left')} badge={out['apps'][a]['badges']['hu'][:1]}")
|
|
json.dump(out, open("../C/20-setup.json", "w"), indent=2, ensure_ascii=False, default=str)
|
|
open("../C/20-setup.log", "w").write("\n".join(w.LOG) + "\n")
|