"""wgerstep.py — R-762 on scratch 9202 (drill catalog), ONE process, through the product. 1 DRILL: wger un-hidden (DRILL only); install fresh at the live definition; seed (the fixture) + read back (C1); 2 a progress photo posted to the gallery (the household's API with its session); its /media URL read: 404 expected (the R-762 fault — the control that the later 200 is the fix, not the test); 3 the login page's own CSS links read: 404 expected; 4 a DRILL commit replaces the compose with the new definition + one ladder entry; sync; the product's guarded Update; 5 after: the seed, the same photo URL (200 + the same byte count), the CSS links (200), wger-files' memory; 6 box verdict JSON; removed through the product (KEEP=1 keeps it). Evidence: ../F/box/step.txt + box-verdict-wger.json.""" import json, os, re, struct, subprocess, sys, tempfile, time, zlib sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts") import box_walk as w import upgrade_fixtures_box as fixtures APP, SUB = "wger", "fitness" NEWDEF = sys.argv[1] HERE = os.path.dirname(os.path.abspath(__file__)) EVD = os.path.join(HERE, "..", "F", "box"); os.makedirs(EVD, exist_ok=True) log = open(f"{EVD}/step.txt", "a", buffering=1) D = "/mnt/5_hdd/felhom.eu/drill/app-catalog-drill" REC = {"app": APP} def say(*a): w.say(*a); log.write(" ".join(map(str, a)) + "\n") def git(*a): return subprocess.run(["git", "-C", D, *a], check=True, capture_output=True, text=True).stdout def png(path, n=64): """A real PNG (n x n, random-ish colour) — the gallery validates the image.""" import secrets rgb = secrets.token_bytes(3) raw = b"".join(b"\x00" + rgb * n for _ in range(n)) def chunk(t, d): return struct.pack(">I", len(d)) + t + d + struct.pack(">I", zlib.crc32(t + d) & 0xffffffff) data = (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", n, n, 8, 2, 0, 0, 0)) + chunk(b"IDAT", zlib.compress(raw)) + chunk(b"IEND", b"")) open(path, "wb").write(data) return len(data) def css_links(): rc, code, page = w.app_curl(SUB, "/en/user/login") links = re.findall(r'(/static/[^"]+\.css)', page or "")[:3] return {l: w.app_curl(SUB, l)[1] for l in links} fx = fixtures.FIXTURES[APP] w.login() if w.stack(APP).get("deployed"): say("wger already installed — removing it first (scratch box)") w.remove(APP) git("pull", "-q", "--rebase", "origin", "main") fy = f"{D}/templates/{APP}/.felhom.yml" s = open(fy).read() if "lifecycle: hidden" in s: open(fy, "w").write(s.replace("lifecycle: hidden", "lifecycle: available", 1)) git("commit", "-q", "-am", "DRILL wger: un-hidden for the R-762 box step (DRILL only)") git("push", "-q", "origin", "main") say("drill:", git("log", "--oneline", "-1").strip()) w.sync_rescan() time.sleep(5) w.sync_rescan() if not w.deploy(APP, SUB): sys.exit(say("RESULT the install did not complete") or 1) before = (w.stack(APP).get("app_config") or {}).get("pinned_images") say(f"[1] installed, pinned={before}") tok = fx.seed(w, SUB, say) if tok is None or not fx.verify(w, SUB, tok, say): sys.exit(say(f"RESULT C1 failed: {getattr(fx, 'tried', '')}") or 1) jar = tempfile.mktemp(prefix="wger-jar-") hdr, why = fx._login(w, SUB, tok["pw"], jar) pic = tempfile.mktemp(prefix="wger-photo-", suffix=".png") size = png(pic) rc, code, out = w.app_curl(SUB, "/api/v2/gallery/", *hdr, "-F", f"image=@{pic};type=image/png", "-F", "date=2026-10-06", "-F", "description=r762", method="POST") say(f"[2] POST /api/v2/gallery/ (a {size} B PNG) -> {code}") img = None try: img = json.loads(out).get("image") except Exception: say(f" body {out[:200]}") path = re.sub(r"^https?://[^/]+", "", img or "") REC["photo_path"], REC["photo_bytes"] = path, size code_b = w.app_curl(SUB, path)[1] if path else None REC["photo_before"] = code_b REC["css_before"] = css_links() say(f"[2] the photo {path} read BEFORE the step -> {code_b}; CSS before {REC['css_before']}") git("pull", "-q", "--rebase", "origin", "main") newc = open(os.path.join(NEWDEF, "docker-compose.yml")).read() open(f"{D}/templates/{APP}/docker-compose.yml", "w").write(newc) to = dict(re.findall(r"^ ([a-z0-9-]+):\s*\n(?:(?! [a-z0-9-]+:\s*\n).*\n)*?\s+image:\s*(\S+)", newc, re.M)) s = open(fy).read() entry = {"from": before, "to": to, "verdict": "proven", "tested_at": "DRILL", "harness_version": 5, "evidence": "DRILL (box proof in progress)", "marks": {"files_may_change": False, "needs_person": None, "memory_tight": False}} s = s.rstrip("\n") + "\n - " + json.dumps(entry) + "\n" s = re.sub(r'^ mem_limit: .*$', ' mem_limit: "416M"', s, count=1, flags=re.M) open(fy, "w").write(s) git("commit", "-q", "-am", f"DRILL wger: the R-762 definition (wger-files) + its ladder entry (box proof)") git("push", "-q", "origin", "main") say("drill:", git("log", "--oneline", "-1").strip(), "| to:", to) w.sync_rescan(APP, to.get("wger-files")) REC["badge_before"] = w.badges(APP) since = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip() res = w.press_update(APP, poll=1, cap_s=1800) for p in res.get("phases", []): log.write(f" phase +{p['t']}s {p['phase']} | err={p['error']}\n") time.sleep(15) read = fx.verify(w, SUB, tok, say) code_a = w.app_curl(SUB, path)[1] if path else None rc, _, body = w.app_curl(SUB, path, "-o", "/dev/null", "-w", "%{size_download}") if path else (1, None, "") REC["photo_after"], REC["photo_after_bytes"] = code_a, body.strip()[-12:] REC["css_after"] = css_links() REC["memory"] = w.guest("docker stats --no-stream --format '{{.Name}} {{.MemUsage}}' | grep -i wger").strip().splitlines() lines = w.guest(f"docker logs --since {since} felhom-controller 2>&1 | grep -E 'update {APP}' | grep -v DEBUG | cut -c1-400") log.write(lines + "\n") st = w.stack(APP); after = (st.get("app_config") or {}).get("pinned_images") REC["badge_after"] = w.badges(APP) say(f"[5] after: phase={res.get('final_phase')} pinned={after} seed={read} photo={code_a} ({REC['photo_after_bytes']}) " f"css={REC['css_after']} mem={REC['memory']}") ok = (res.get("final_phase") == "done" and read and after == to and code_a == "200" and REC["css_after"] and all(c == "200" for c in REC["css_after"].values())) verdict = {"app": APP, "venue": "box 9202 (drill catalog), the product's guarded Update (R-762 definition step)", "from": before, "to": after, "verdict": "proven" if ok else "failed", "seed_read_before": True, "seed_read_after": read, "healthy_after": st.get("state") == "running", "duration_s": res.get("duration_s"), "final_phase": res.get("final_phase"), "measured_at": since, "evidence": "felhom.eu/documentation/audits/design-build-2026-10-06/F/box/step.txt", "r762": REC} json.dump(verdict, open(f"{EVD}/box-verdict-{APP}.json", "w"), indent=2) say(f"RESULT {verdict['verdict']}") for f in (jar, pic): if os.path.exists(f): os.unlink(f) if not os.environ.get("KEEP"): say(f"remove -> {w.remove(APP)}")