a7971c4822
gates / gates (push) Successful in 25s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
48 lines
2.3 KiB
Python
48 lines
2.3 KiB
Python
"""Read-only: for each deployed app on a demo box's 9201, what the automatic leg would do tonight — computed from the
|
|
box's own app.yaml pins and the box's own catalog clone, with the leg's rules (proven, needs_person, files_may_change,
|
|
older-than-ladder, current). Usage: python3 d_waiting.py <ssh-host> <out.json>"""
|
|
import json, subprocess, sys
|
|
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
|
|
import ladder
|
|
host, outp = sys.argv[1], sys.argv[2]
|
|
def g(cmd):
|
|
return subprocess.run(["ssh", host, f"export LC_ALL=C; pct exec 9201 -- bash -c {json.dumps(cmd)}"], capture_output=True, text=True, timeout=120).stdout
|
|
VOL = "/var/lib/docker/volumes/felhom-controller-data/_data/data/catalog-cache/templates"
|
|
stacks = g("ls /opt/docker/stacks").split()
|
|
res = {}
|
|
for a in stacks:
|
|
app = g(f"cat /opt/docker/stacks/{a}/app.yaml 2>/dev/null")
|
|
if "deployed: true" not in app:
|
|
continue
|
|
pins, inst, sec = {}, {}, None
|
|
for l in app.splitlines():
|
|
if not l.startswith(" "):
|
|
sec = l.rstrip(":").strip()
|
|
continue
|
|
if sec == "pinned_images" and l.startswith(" ") and not l.startswith(" ") and ":" in l:
|
|
k, v = l.strip().split(":", 1); pins[k] = v.strip().strip('"')
|
|
fy = g(f"cat {VOL}/{a}/.felhom.yml 2>/dev/null")
|
|
E, _, err = ladder.parse(fy)
|
|
cat = ladder.images_in(g(f"cat {VOL}/{a}/docker-compose.yml 2>/dev/null"))
|
|
verdict = None
|
|
if not pins:
|
|
verdict = "unpinned"
|
|
elif pins == cat:
|
|
verdict = "current (pin = catalog head)"
|
|
elif not E:
|
|
verdict = "no_test_record"
|
|
else:
|
|
idx = max([i for i, e in enumerate(E) if e["from"] == pins] or [-1])
|
|
if idx < 0:
|
|
verdict = "no_test_record (at head)" if E[-1]["to"] == pins else "older_than_ladder"
|
|
else:
|
|
e = E[idx]
|
|
m = e.get("marks") or {}
|
|
if e["verdict"] != "proven": verdict = "not_proven"
|
|
elif m.get("needs_person"): verdict = "needs_person"
|
|
elif m.get("files_may_change"): verdict = "files_may_change (needs a fresh whole copy)"
|
|
else: verdict = "STEP WAITING: " + json.dumps(e["to"])
|
|
res[a] = {"pinned": pins, "catalog": cat, "ladder_len": len(E), "leg": verdict}
|
|
print(f"{a}: {verdict}")
|
|
json.dump(res, open(outp, "w"), indent=2)
|