Release version management: single VERSION file as source of truth
- 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:
1 parent
f3f1b37221
commit
fb980d12b4
17 files changed
+422
-37
No files matched your search
@@ -431,6 +431,61 @@ async def upload_file(
|
||||
# ── Lifecycle ───────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_hello_ack_advertises_app_version_and_sse_header_is_stored(
|
||||
adapter, ws_client
|
||||
):
|
||||
"""hello.ack server_caps carries the gateway's release version, and the
|
||||
app's X-Iris-App-Version header (sent on the SSE open) is stored in the
|
||||
device registry's caps JSON (docs/04)."""
|
||||
client, ack = ws_client
|
||||
caps = ack["payload"]["server_caps"]
|
||||
assert caps["app_version"] == sys.modules["iris_plugin_under_test"].version.plugin_version()
|
||||
|
||||
# The test client does not send the header; open a second SSE stream that
|
||||
# does, and check the device registry picked it up.
|
||||
from http.client import HTTPConnection
|
||||
|
||||
conn = HTTPConnection("127.0.0.1", adapter._http_server.bound_port, timeout=5)
|
||||
conn.request(
|
||||
"GET",
|
||||
"/v1/events?cursor=0",
|
||||
headers={
|
||||
"Authorization": f"Bearer {TOKEN}",
|
||||
"X-Iris-Device": DEVICE_ID,
|
||||
"X-Iris-App-Version": "9.9.9",
|
||||
},
|
||||
)
|
||||
resp = conn.getresponse()
|
||||
assert resp.status == 200
|
||||
resp.read(1) # don't hold the stream
|
||||
conn.close()
|
||||
|
||||
device = adapter._devices.get(DEVICE_ID)
|
||||
assert device is not None
|
||||
assert device["caps"].get("app_version") == "9.9.9"
|
||||
|
||||
# A reconnect from an OLD app build (no X-Iris-App-Version header) must
|
||||
# NOT wipe the previously stored version — the caps merge preserves it.
|
||||
conn2 = HTTPConnection("127.0.0.1", adapter._http_server.bound_port, timeout=5)
|
||||
conn2.request(
|
||||
"GET",
|
||||
"/v1/events?cursor=0",
|
||||
headers={
|
||||
"Authorization": f"Bearer {TOKEN}",
|
||||
"X-Iris-Device": DEVICE_ID,
|
||||
},
|
||||
)
|
||||
resp2 = conn2.getresponse()
|
||||
assert resp2.status == 200
|
||||
resp2.read(1)
|
||||
conn2.close()
|
||||
|
||||
device2 = adapter._devices.get(DEVICE_ID)
|
||||
assert device2 is not None
|
||||
assert device2["caps"].get("app_version") == "9.9.9"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_disconnect_broadcasts_status_restarting(adapter):
|
||||
"""Teardown broadcasts ``status{state=restarting}`` before closing the
|
||||
|
||||
Reference in new issue
Block a user