Release version management: single VERSION file as source of truth
CI / Gateway plugin tests (push) Successful in 5m19s
CI / Kotlin tests (android host + desktop) (push) Successful in 7m0s

- VERSION at repo root (0.1.2); bump it to cut a release
- App: generated AppVersion.kt (config-cache-safe Gradle task with
  VERSION as declared input) shown in Settings; sent to the gateway
  via X-Iris-App-Version header on the SSE open
- Gateway: reports its own version in hello.ack server_caps.app_version
  (read from the repo-root VERSION via the plugin symlink); stores the
  app's version in the device registry caps (merge, not overwrite, so
  an old app reconnecting without the header doesn't wipe it)
- Settings: app + gateway version rows, mismatch hint, and a best-effort
  Gitea latest-release check (ReleaseCheck) with an 'update available' hint
- Release workflow: reads VERSION from the repo (no manual input), with
  a guard against an empty file
- Docs: frames.schema.json + 04-wire-protocol.md updated for app_version
This commit is contained in:
ARIA committed 2026-08-25 14:42:39 +02:00
1 parent f3f1b37221
commit fb980d12b4
17 files changed
+422 -37

No files matched your search

+16 -2
View File
@@ -490,11 +490,20 @@ class HttpServer:
device_name = (handler.headers.get("X-Iris-Device-Name") or "").strip()[:120]
fcm_token = handler.headers.get("X-Iris-Fcm-Token") or None
ntfy_topic = handler.headers.get("X-Iris-Ntfy-Topic") or None
# App release version (the repo-root VERSION baked into the build);
# stored in the device registry's caps JSON so `hermes` can see which
# app version each device runs (old-version awareness). A missing
# header (old app build) must not wipe a previously stored version,
# so merge over the existing caps instead of replacing them.
app_version = (handler.headers.get("X-Iris-App-Version") or "").strip()[:40]
try:
existing_caps = dict(self._devices.get(device_id) or {}).get("caps") or {}
if app_version:
existing_caps["app_version"] = app_version
self._devices.upsert(
device_id,
device_name or device_id,
None,
existing_caps or None,
fcm_token,
ntfy_topic,
)
@@ -523,7 +532,12 @@ class HttpServer:
# lifecycle is the primary "is the device connected?" signal
# for debugging flaky links — a gap here is invisible at the
# gateway's default log level.
logger.info("iris: SSE stream opened: %s (cursor=%d)", device_id, cursor)
logger.info(
"iris: SSE stream opened: %s (cursor=%d, app_version=%s)",
device_id,
cursor,
app_version or "?",
)
# 1. Catch-up from the outbox (id = cursor; the envelope also
# carries the cursor for the app's push dedupe).
max_cursor = cursor