feat: watch the release page of off-forge profiles

About forty profiles describe projects published outside any forge (Altirra, ePSXe, DraStic, mednafen, xroar). profile_sync skipped them as an unsupported host and check_freshness reported the same UNKNOWN line every run, so a new release adding a firmware went unseen. A profile can now name the page announcing its releases and a pattern for the version: --check-version reads it, and check_freshness --profiles calls the profile stale when the page announces another version than core_version.
This commit is contained in:
Abdessamad Derraz committed 2026-10-20 04:28:27 +02:00
1 parent d43aa5354c
commit 282f8d5d0a
7 files changed
+102

No files matched your search

+10
View File
@@ -1097,6 +1097,16 @@ def summarize_profile_sync(report: object) -> list[Finding]:
if not isinstance(row, dict):
continue
name = str(row.get("name") or "?")
if row.get("skipped") and row.get("page_version"):
# Off any forge, the release page is the feed: a version there
# beyond the declared one is a release the profile does not cover.
declared, announced = str(row.get("declared_version") or ""), str(row["page_version"])
findings.append(Finding(
"profiles", name, OK if announced == declared else STALE,
local=declared, remote=announced,
detail="" if announced == declared else "a newer release is announced",
))
continue
if row.get("skipped"):
findings.append(Finding("profiles", name, UNKNOWN, detail=str(row["skipped"])))
continue
+31
View File
@@ -1114,6 +1114,10 @@ class ProfileReport:
# Repositories whose pin is no ancestor of the branch the profile
# tracks: history was rewritten, or the pin lies on another branch.
orphaned: list[str] = field(default_factory=list)
# A profile off any forge names the page announcing its releases
# (version_source): the version read there, beside the one declared.
declared_version: str | None = None
page_version: str | None = None
# Skips that say the profile cannot be checked as written: a repository
# that no longer answers, no pin to read, a death the forge contradicts.
@@ -2032,6 +2036,11 @@ def format_report(report: ProfileReport, changed_only: bool = False) -> str:
lines.append(f" not read (unsupported host): {url}")
for line in report.orphaned:
lines.append(f" orphaned pin: {line}")
if report.declared_version or report.page_version:
lines.append(
f" version page: declared {report.declared_version or '-'}, "
f"announced {report.page_version or 'unreadable'}"
)
if report.skipped:
lines.append(f" skipped: {report.skipped}")
return "\n".join(lines)
@@ -2137,6 +2146,25 @@ def check_version(
)
def page_version(profile: dict, cache_dir: str, offline: bool) -> str | None:
"""The latest version a profile's version_source page announces.
Forty-odd profiles live off any forge: profile_sync could only say
"unsupported host", and a new Altirra or mednafen release went unseen.
"""
source = profile.get("version_source") or {}
url, pattern = str(source.get("url") or ""), str(source.get("pattern") or "")
if not url or not pattern:
return None
try:
text = upstream.fetch_page(url, cache_dir, offline)
except upstream.UpstreamError as exc:
print(f"{profile.get('emulator', '?')}: {url}: {exc}", file=sys.stderr)
return None
match = re.search(pattern, text or "")
return match.group(1) if match else None
def version_warning(report: VersionReport | None, pin: str | None) -> str | None:
"""Flag a declared version that names a tag away from the resolved pin."""
if report is None or not report.tag_commit or not pin:
@@ -3810,6 +3838,9 @@ def main() -> None:
name=name, entries=[], counts={}, skipped=f"upstream error: {exc}"
)
failed.add(name)
if args.check_version and profile.get("version_source"):
report.declared_version = str(profile.get("core_version") or "") or None
report.page_version = page_version(profile, args.cache_dir, args.offline)
reports.append(report)
if writes:
try:
+20
View File
@@ -400,6 +400,26 @@ def _api(
return payload
def fetch_page(url: str, cache_dir: str, offline: bool) -> str | None:
"""A web page's text, cached like a moving API answer (MOVING_TTL).
A project published outside a forge (Altirra, mednafen, ePSXe) says its
version on a download page: that page is the only release feed there is.
"""
key = hashlib.sha256(url.encode()).hexdigest()
target = Path(cache_dir) / "_pages" / f"{key}.txt"
if target.is_file():
fresh = time.time() - target.stat().st_mtime <= MOVING_TTL
if fresh or offline:
raw = target.read_text(encoding="utf-8")
return None if raw == ABSENT else raw
if offline:
return None
text = _http_text(url)
write_cache(target, ABSENT if text is None else text)
return text
def _project(repo: Repo) -> str:
return urllib.parse.quote(f"{repo.owner}/{repo.name}", safe="")