Skip to content

Commit 612f602

Browse files
fix(gui): CI status reads the newest published ISO from the Archive itself (#308)
The Internet Archive row reconstructed the identifier from the last SUCCESSFUL dispatch run (publish-job start date). Twice in one week that went stale, because an IA publish can land even when the run reports failure (2026-07-06 multipart-ETag false-fail, 2026-07-10 rationing verify-timeout): the row showed margine-live-iso-20260705 while 20260711 was live. Now the row asks archive.org directly (advancedsearch, newest margine-live-iso-* item) and reads the source commit from the item description (written by the publish step), which also gives 'Changes since last publish' an honest base: it now diffs against the commit of the newest PUBLISHED ISO, falling back to the old gh-derived base only while the Archive metadata has not loaded. The run-metadata reconstruction (_derive_ia_identifier) is deleted. Verified against live data: the search returns 20260711, the sha parses (5891c51), and the diff lists the 3 commits merged after that dispatch.
1 parent 5b251fa commit 612f602

1 file changed

Lines changed: 65 additions & 77 deletions

File tree

  • tools/iso-builder/mib

tools/iso-builder/mib/ci.py

Lines changed: 65 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import datetime
1414
import json
1515
import os
16+
import re
1617
import shutil
1718

1819
import gi
@@ -94,7 +95,8 @@ def _fmt_date(created):
9495
class CiPage:
9596
def __init__(self, win):
9697
self.win = win
97-
self._ia_link = None # derived archive.org details URL
98+
self._ia_link = None # archive.org details URL of the newest item
99+
self._ia_sha = None # source commit of the newest published ISO
98100
self._pub_url = None # newest successful publish run's URL
99101
self._status_busy = False # a dashboard refresh is in flight
100102
self._status_pending = 0
@@ -271,6 +273,7 @@ def refresh(self):
271273
for row in (self.stable_row, self.pubstat_row, self.ia_row):
272274
row.set_subtitle("checking…")
273275
self._ia_link = None
276+
self._ia_sha = None
274277
self._pub_url = None
275278
self.ia_copy_btn.set_visible(False)
276279
self.ia_open_btn.set_visible(False)
@@ -347,83 +350,57 @@ def viewed(ok2, out2, err2):
347350
"--workflow", "build-disk.yml", "--event", "workflow_dispatch",
348351
"--limit", "1", "--json", "databaseId,createdAt"], listed)
349352

353+
# Until 2026-07-11 this row RECONSTRUCTED the identifier from the last
354+
# SUCCESSFUL dispatch run (publish-job start date). Twice in a week that
355+
# showed days-old items, because IA publishes can complete even when the
356+
# run reports failure (2026-07-06 multipart-ETag false-fail, 2026-07-10
357+
# rationing verify-timeout): the last "successful" run was much older
358+
# than the last actually-published ISO. Ask the Archive itself instead:
359+
# the newest margine-live-iso-* item IS the truth, and its description
360+
# carries the source commit (written by the publish step), which also
361+
# gives "Changes since last publish" an honest base.
350362
def _q_ia(self):
351-
def listed(ok, out, err):
352-
runs = _runs(out) if ok else []
353-
if not runs:
363+
def searched(ok, out, _err):
364+
ident = None
365+
if ok and out.strip():
366+
try:
367+
docs = json.loads(out).get("response", {}).get("docs", [])
368+
if docs:
369+
ident = docs[0].get("identifier")
370+
except ValueError:
371+
ident = None
372+
if not ident:
354373
self.ia_row.set_subtitle(
355-
("unavailable — " + _errline(err)) if not ok
356-
else "no successful publish run yet")
374+
"archive.org unreachable — retry with the refresh button")
357375
self._done_one()
358376
return
359-
self._pub_url = runs[0].get("url") or ""
377+
self._ia_link = f"https://archive.org/details/{ident}"
378+
self.ia_row.set_subtitle(_esc(ident))
379+
self.ia_copy_btn.set_visible(True)
360380

361-
def viewed(ok2, out2, _err2):
381+
def got_meta(ok2, out2, _err2):
362382
try:
363-
data = None
364383
if ok2 and out2.strip():
365-
try:
366-
data = json.loads(out2)
367-
except ValueError:
368-
data = None
369-
ident = self._derive_ia_identifier(data)
370-
if ident:
371-
self._ia_link = f"https://archive.org/details/{ident}"
372-
self.ia_row.set_subtitle(_esc(ident))
373-
self.ia_copy_btn.set_visible(True)
374-
else:
375-
self.ia_row.set_subtitle(
376-
"No Archive link for this run "
377-
"(non-stable variant?) — open the run page")
378-
self.ia_open_btn.set_visible(bool(self._pub_url))
384+
desc = (json.loads(out2).get("metadata", {})
385+
or {}).get("description", "")
386+
m = re.search(r"commit ([0-9a-f]{7,40})", desc)
387+
if m:
388+
self._ia_sha = m.group(1)
389+
except ValueError:
390+
pass
379391
finally:
380392
self._done_one()
381393

382-
core.gh(["run", "view", str(runs[0]["databaseId"]),
383-
"--repo", core.GH_REPO, "--json", "jobs"], viewed)
384-
385-
core.gh(["run", "list", "--repo", core.GH_REPO,
386-
"--workflow", "build-disk.yml", "--event", "workflow_dispatch",
387-
"--status", "success", "--limit", "1",
388-
"--json", "databaseId,url"], listed)
394+
core.spawn_collect(
395+
["curl", "-s", "--max-time", "15",
396+
f"https://archive.org/metadata/{ident}"], got_meta)
389397

390-
@staticmethod
391-
def _derive_ia_identifier(data):
392-
"""Reproduce the ia_upload step of .github/workflows/build-disk.yml:
393-
394-
DATE_TAG="$(date -u +%Y%m%d)" # taken IN the publish_ia job
395-
VSLUG="live" for stable, else the raw tag (e.g. "nvidia")
396-
IDENTIFIER="margine-${VSLUG}-iso-${DATE_TAG}"
397-
→ https://archive.org/details/$IDENTIFIER
398-
399-
What is reconstructable from run data:
400-
* DATE_TAG — the 'Publish ISO' JOB's startedAt (UTC day), NOT the
401-
run's createdAt: `gh run rerun --failed <id>` re-runs just
402-
publish_ia days later and DATE_TAG is re-derived then.
403-
* variant — workflow_dispatch inputs are not exposed by the runs
404-
API, but install_gate's `if:` runs it exactly when image_tag is
405-
''/'stable', so gate success => stable => VSLUG "live". A skipped
406-
gate means a non-stable tag whose VALUE is unrecoverable — do not
407-
guess (return None → the run-page fallback button).
408-
matrix.image is single-entry "margine" (gaming variant retired
409-
2026-06-06), so the identifier prefix is a constant.
410-
"""
411-
gate_s, gate_c = _job(data, "Automated install gate")
412-
if gate_s != "completed" or gate_c != "success":
413-
return None
414-
pub = None
415-
for j in (data or {}).get("jobs", []):
416-
if j.get("name", "").startswith("Publish ISO"):
417-
pub = j
418-
break
419-
if pub is None or pub.get("conclusion") != "success":
420-
return None
421-
ts = core.iso_ts(pub.get("startedAt") or "")
422-
if ts <= 0:
423-
return None
424-
date_tag = datetime.datetime.fromtimestamp(
425-
ts, datetime.timezone.utc).strftime("%Y%m%d")
426-
return f"margine-live-iso-{date_tag}"
398+
core.spawn_collect(
399+
["curl", "-sg", "--max-time", "15",
400+
"https://archive.org/advancedsearch.php"
401+
"?q=identifier:margine-live-iso-*"
402+
"&fl[]=identifier&sort[]=identifier+desc&rows=1&output=json"],
403+
searched)
427404

428405
def on_copy_ia(self, _btn):
429406
if not self._ia_link:
@@ -454,16 +431,7 @@ def on_open_pubstat_run(self, _btn):
454431
def on_changelog(self, _btn):
455432
self.chlog_btn.set_sensitive(False)
456433

457-
def listed(ok, out, err):
458-
runs = _runs(out) if ok else []
459-
if not runs or not runs[0].get("headSha"):
460-
self.chlog_btn.set_sensitive(True)
461-
self.win.toast(("gh failed: " + _errline(err)) if not ok else
462-
"No successful publish run found — nothing to diff against")
463-
return
464-
sha = runs[0]["headSha"]
465-
since = _fmt_date(runs[0].get("createdAt", ""))
466-
434+
def diff_from(sha, since):
467435
def got(ok2, out2, err2):
468436
self.chlog_btn.set_sensitive(True)
469437
if not ok2:
@@ -478,6 +446,26 @@ def got(ok2, out2, err2):
478446
core.spawn_collect(["git", "-C", core.REPO_ROOT, "log",
479447
"--oneline", f"{sha}..HEAD"], got)
480448

449+
# Preferred base: the source commit of the newest PUBLISHED ISO,
450+
# read from the Archive item itself by _q_ia (truthful even when
451+
# the publish run reported failure but the upload landed, as on
452+
# 2026-07-06 and 2026-07-10). Identifier date doubles as "since".
453+
if self._ia_sha and self._ia_link:
454+
diff_from(self._ia_sha, self._ia_link.rsplit("-", 1)[-1])
455+
return
456+
457+
# Fallback (Archive metadata not loaded yet / unreachable): the
458+
# last successful dispatch run's head commit, the pre-2026-07-11
459+
# behavior.
460+
def listed(ok, out, err):
461+
runs = _runs(out) if ok else []
462+
if not runs or not runs[0].get("headSha"):
463+
self.chlog_btn.set_sensitive(True)
464+
self.win.toast(("gh failed: " + _errline(err)) if not ok else
465+
"No successful publish run found — nothing to diff against")
466+
return
467+
diff_from(runs[0]["headSha"], _fmt_date(runs[0].get("createdAt", "")))
468+
481469
core.gh(["run", "list", "--repo", core.GH_REPO,
482470
"--workflow", "build-disk.yml", "--event", "workflow_dispatch",
483471
"--status", "success", "--limit", "1",

0 commit comments

Comments
 (0)