Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.9 KiB
GUI build-version badge
Date: 2026-06-03
Status: approved (design)
Area: VERSION, build.sh, Dockerfile, src/presa_mano/version.py, src/presa_mano/main.py, web/index.html, web/app.js
Problem
src/ and web/ are baked into the Docker image (no bind-mount), so code changes only take effect after an image rebuild. There is no visible indicator of which build the running container serves, so a stale deployment is invisible — exactly the confusion behind "non vedo modifiche alla GUI". We want a version shown in the GUI header that increments on every build, plus the build time.
Decisions (locked)
- Version string:
V<major>.<minor>.<patch>(e.g.V1.0.1), patch auto-increments every build; build time shown too. - Injection: a build wrapper script passes the version + build time as Docker build args.
- Placement: a pill badge in the GUI header, build time in its tooltip.
Components
VERSION (tracked file, repo root presa-mano/VERSION)
Plain text major.minor.patch, initialized to 1.0.0. Single source of truth for the number; persists across builds via the file on disk. The build script rewrites it (left as an uncommitted working-tree edit — the script does NOT auto-commit).
build.sh (repo root, executable)
1. cd to script dir; ensure VERSION exists (else seed 1.0.0)
2. parse current major.minor.patch
3. bump: default patch++; arg `minor` → minor++/patch=0; `major` → major++/minor=patch=0
4. write new VERSION
5. BUILD_TIME = $(date -u +"%Y-%m-%d %H:%M UTC")
6. (cd compose dir) docker compose build --build-arg APP_VERSION=<new> --build-arg BUILD_TIME="<ts>" presa-mano
7. docker compose up -d presa-mano
8. echo deployed version
The compose file lives one level up (/opt/docker/robotserver/docker-compose.yml); the script resolves that dir and runs compose there. Usage: ./build.sh [patch|minor|major] (default patch).
Dockerfile
Add late (before CMD), so changing them doesn't bust the dependency layers:
ARG APP_VERSION=dev
ARG BUILD_TIME=unknown
ENV APP_VERSION=${APP_VERSION}
ENV BUILD_TIME=${BUILD_TIME}
Also COPY VERSION ./ (cheap, makes the file fallback work inside the container too).
src/presa_mano/version.py
def build_info() -> dict:
"""{'version', 'build_time'}. Prefer env (set at Docker build); fall back to
the VERSION file + '(local)' for `uv run` dev."""
version=APP_VERSIONenv if set, else<VERSION file contents> (local)(or0.0.0 (local)if the file is unreadable).build_time=BUILD_TIMEenv if set, else(local).VERSIONfile resolved asPath(__file__).resolve().parents[2] / "VERSION".
src/presa_mano/main.py
from .version import build_info
@app.get("/api/version", tags=["meta"])
def version() -> dict:
return build_info()
web/index.html
In the header, next to #activeRobotPill, add:
<span class="active-robot-pill" id="appVersionPill" title="versione build">V—</span>
(reuses the existing pill styling).
web/app.js
Add and call once at startup (alongside refreshHomeRobotSelector()):
async function refreshAppVersion() {
try {
const r = await api('GET', '/api/version');
const pill = $('appVersionPill');
if (pill) { pill.textContent = `V${r.version}`; pill.title = `build ${r.build_time}`; }
} catch {}
}
refreshAppVersion();
Testing
- Unit test
tests/test_version.pyforbuild_info():- env set (
APP_VERSION,BUILD_TIMEvia monkeypatch) → returns exactly those. - env unset (monkeypatch.delenv) →
versionends with(local)and contains theVERSIONfile content;build_time == "(local)".
- env set (
- Frontend: manual — after
./build.shthe badge increments and the tooltip shows the new build time.
Out of scope (YAGNI)
- git SHA / dirty flag (user chose version+time only).
- Auto-commit of the VERSION bump.
- Frontend-vs-backend version mismatch warning.