feat(web): footer build SHA shows "(latest)" or "(N behind)"

Footer now compares the running SOURCE_COMMIT against origin/main via
Gitea's compare API and renders "build <sha> (latest)" when up to date
or "build <sha> (N behind)" otherwise — so it's obvious from any page
whether the deploy is current.

Per-SHA cache (60s TTL, 1.5s timeout) keeps the lookup off the hot path
in steady state. Network or parse errors return None and the parens
suffix is just hidden — the SHA itself always renders, so a flaky git
host can never break the layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
EiSiMo 2026-04-23 11:21:24 +02:00
parent 20872b2383
commit b5b4908ee7
3 changed files with 47 additions and 1 deletions

37
web/version.py Normal file
View file

@ -0,0 +1,37 @@
"""How far behind origin/main the running build is.
Hits Gitea's compare endpoint with a short per-SHA cache. Network errors
return None and the footer just hides the status the SHA itself still
renders, so a flaky git host never breaks the page.
"""
import logging
import time
import requests
_GITEA_REPO_API = "https://git.moritz.run/api/v1/repos/moritz/lazyflat"
_CACHE_TTL = 60.0
_HTTP_TIMEOUT = 1.5
_cache: dict[str, tuple[int, float]] = {}
logger = logging.getLogger("web.version")
def commits_behind_main(sha: str) -> int | None:
"""Return how many commits `sha` is behind origin/main, or None on error/dev."""
if not sha or sha == "dev":
return None
now = time.monotonic()
cached = _cache.get(sha)
if cached and (now - cached[1]) < _CACHE_TTL:
return cached[0]
try:
r = requests.get(f"{_GITEA_REPO_API}/compare/{sha}...main", timeout=_HTTP_TIMEOUT)
r.raise_for_status()
behind = int(r.json().get("total_commits", 0))
except (requests.RequestException, ValueError, KeyError) as e:
logger.debug("commits_behind_main fetch failed: %s", e)
return cached[0] if cached else None
_cache[sha] = (behind, now)
return behind