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:
parent
20872b2383
commit
b5b4908ee7
3 changed files with 47 additions and 1 deletions
|
|
@ -24,6 +24,7 @@ import notifications
|
||||||
from apply_client import ApplyClient, _row_to_profile
|
from apply_client import ApplyClient, _row_to_profile
|
||||||
from auth import issue_csrf_token
|
from auth import issue_csrf_token
|
||||||
from settings import APPLY_FAILURE_THRESHOLD, GIT_COMMIT, INTERNAL_API_KEY
|
from settings import APPLY_FAILURE_THRESHOLD, GIT_COMMIT, INTERNAL_API_KEY
|
||||||
|
from version import commits_behind_main
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("web")
|
logger = logging.getLogger("web")
|
||||||
|
|
@ -108,6 +109,13 @@ def require_internal(x_internal_api_key: str | None = Header(default=None)) -> N
|
||||||
|
|
||||||
|
|
||||||
def base_context(request: Request, user, active_tab: str) -> dict:
|
def base_context(request: Request, user, active_tab: str) -> dict:
|
||||||
|
behind = commits_behind_main(GIT_COMMIT)
|
||||||
|
if behind is None:
|
||||||
|
git_status = ""
|
||||||
|
elif behind == 0:
|
||||||
|
git_status = "latest"
|
||||||
|
else:
|
||||||
|
git_status = f"{behind} behind"
|
||||||
return {
|
return {
|
||||||
"request": request,
|
"request": request,
|
||||||
"user": user,
|
"user": user,
|
||||||
|
|
@ -116,6 +124,7 @@ def base_context(request: Request, user, active_tab: str) -> dict:
|
||||||
"is_admin": bool(user["is_admin"]),
|
"is_admin": bool(user["is_admin"]),
|
||||||
"git_commit": GIT_COMMIT,
|
"git_commit": GIT_COMMIT,
|
||||||
"git_commit_short": GIT_COMMIT[:7] if GIT_COMMIT and GIT_COMMIT != "dev" else GIT_COMMIT,
|
"git_commit_short": GIT_COMMIT[:7] if GIT_COMMIT and GIT_COMMIT != "dev" else GIT_COMMIT,
|
||||||
|
"git_status": git_status,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
Mit ♥ programmiert für Annika
|
Mit ♥ programmiert für Annika
|
||||||
{% if git_commit_short %}
|
{% if git_commit_short %}
|
||||||
<span class="mx-1">·</span>
|
<span class="mx-1">·</span>
|
||||||
<span title="{{ git_commit }}">build {{ git_commit_short }}</span>
|
<span title="{{ git_commit }}">build {{ git_commit_short }}{% if git_status %} ({{ git_status }}){% endif %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</footer>
|
</footer>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
37
web/version.py
Normal file
37
web/version.py
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue