42bf40bd2c
gates / gates (push) Successful in 29s
- Twelve steps published on both venues (catalog): first ladders for calibre-web, gitea, wger, crafty-controller, uptime-kuma, zipline (two steps); within-major emby, ghost, home-assistant, outline, rallly. immich's step v3.0.3 -> v3.2.2 re-proven at 768M (Part D). - Part C: the night leg skips a digest-only change AND the catalog never records a same-tag re-test, so a same-name upstream fix reaches no box. Row R-740; the decision in STATUS; `09` decision 30 carries a dated note (the decision itself unchanged). - Rows: 369 -> 377. Opened R-735..R-742; closed R-735, R-738, R-742; narrowed R-462, R-624, R-446, R-440, R-734, R-732. The currency audit gains §1b (and corrects its 31+1 to 30+2). Evidence: documentation/audits/more-night-apps-2026-09-30/. Report: REPORT-more-night-apps-2026-09-30.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
51 lines
2.8 KiB
Python
51 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""floatpush.py — Part C (2026-09-30): for every FLOATING line in the live catalog (the currency audit's definition:
|
|
fewer than three numbers in the tag's first version run), (1) does the registry serve today the digest the newest
|
|
ladder entry TESTED for it, and (2) when was the tag last pushed (Docker Hub `tag_last_pushed`; ghcr/lscr have no
|
|
anonymous timestamp — said, not guessed). Read-only; the audit's helpers are imported."""
|
|
import importlib.util, json, os, re, sys, datetime, requests
|
|
CAT = "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu"
|
|
A = "/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/catalog-currency-2026-09-30/00-currency.py"
|
|
sys.argv = ["x", CAT, "/dev/null"]
|
|
s = importlib.util.spec_from_file_location("c", A); c = importlib.util.module_from_spec(s); s.loader.exec_module(c)
|
|
sys.path.insert(0, CAT + "/scripts"); import ladder
|
|
now = datetime.datetime.now(datetime.timezone.utc)
|
|
print(f"# floating lines in the live catalog, read {now.isoformat(timespec='seconds')}")
|
|
lines = {}
|
|
for app in sorted(os.listdir(CAT + "/templates")):
|
|
d = f"{CAT}/templates/{app}"
|
|
imgs = ladder.images_in(open(d + "/docker-compose.yml").read())
|
|
entries, _, _ = ladder.parse(open(d + "/.felhom.yml").read())
|
|
for svc, ref in imgs.items():
|
|
repo, tag = c.split_ref(ref)
|
|
if not c.is_floating(tag):
|
|
continue
|
|
tested = None
|
|
for e in reversed(entries or []):
|
|
if e.get("to", {}).get(svc) == ref:
|
|
tested = (e.get("digest") or {}).get(svc); break
|
|
lines.setdefault(ref, []).append((app, svc, tested))
|
|
recent = 0
|
|
for ref, uses in sorted(lines.items()):
|
|
repo, tag = c.split_ref(ref); host, r = c.host_repo(repo)
|
|
try:
|
|
now_d = c.digest(host, r, tag)
|
|
now_d = now_d[0] if isinstance(now_d, tuple) else now_d
|
|
except Exception as e:
|
|
now_d = f"ERR {e!r}"[:60]
|
|
pushed = "no anonymous timestamp on " + host
|
|
if host == "registry-1.docker.io":
|
|
try:
|
|
j = requests.get(f"https://hub.docker.com/v2/repositories/{r}/tags/{tag}", timeout=30).json()
|
|
pushed = j.get("tag_last_pushed") or j.get("last_updated") or "?"
|
|
age = (now - datetime.datetime.fromisoformat(pushed.replace("Z", "+00:00"))).days
|
|
pushed = f"{pushed} ({age} days ago)"
|
|
if age <= 30: recent += 1
|
|
except Exception as e:
|
|
pushed = f"ERR {e!r}"[:80]
|
|
print(f"\n{ref} last pushed: {pushed}\n registry digest today: {str(now_d)[:19]}")
|
|
for app, svc, tested in uses:
|
|
st = "no ladder digest" if not tested else ("= tested" if tested == now_d else f"MOVED since the test (tested {tested[:19]})")
|
|
print(f" {app}/{svc}: {st}")
|
|
print(f"\n# {len(lines)} distinct floating lines; Docker Hub lines pushed within 30 days: {recent}")
|