commit ca036bce54a881736cf8a97caeb845875f522a50 equwal <truex@equwal.com> 2026-09-20 21:22:51 -0700 Report the running release in /healthz; document tagging
README.md | 12 ++++++++++++ backend/main.py | 17 +++++++++++++++++ 2 files changed, 29 insertions(+)
diff --git a/README.md b/README.md index f95907b..6d8a9ae 100644 --- a/README.md +++ b/README.md @@ -373,6 +373,18 @@ once you are registered somewhere). --- +## Releases + +Every deploy is a tag (`vMAJOR.MINOR.PATCH`) with a GitHub release; `/healthz` +reports which one is running. Tag, push, then deploy that tag: + +```bash +git tag -a v2.0.1 -m "what changed" && git push origin main --tags +gh release create v2.0.1 --notes "what changed" +``` + +--- + ## Scaling out Everything environment-specific is an env var with a localhost default. See diff --git a/backend/main.py b/backend/main.py index 484ace4..bceb993 100644 --- a/backend/main.py +++ b/backend/main.py @@ -10,6 +10,7 @@ from __future__ import annotations import logging import mimetypes +import subprocess from contextlib import asynccontextmanager from fastapi import FastAPI @@ -30,6 +31,21 @@ log = logging.getLogger("subplz.web") FRONTEND = ROOT / "frontend" + +def _version() -> str: + """The release this checkout is: the nearest git tag, e.g. v2.0.0 or + v2.0.0-3-gabc1234 when it is ahead of one. Answers "what is deployed?".""" + try: + return subprocess.run( + ["git", "-c", f"safe.directory={ROOT.as_posix()}", "describe", "--tags", "--always", "--dirty"], + cwd=ROOT, capture_output=True, text=True, timeout=10, check=True, + ).stdout.strip() or "unknown" + except (subprocess.SubprocessError, OSError): + return "unknown" + + +VERSION = _version() + # Python takes these from the OS, and Windows gets both wrong. A module served # as text/plain is refused outright, and WebAssembly will not stream-compile. mimetypes.add_type("text/javascript", ".js") @@ -108,6 +124,7 @@ async def cross_origin_isolation(request, call_next): def healthz(): return { "ok": True, + "version": VERSION, "languages": len(all_languages()), "queue_backend": settings.queue_backend, "queue_depth": queue.depth(),