Files
iris_x_hermes/gateway-plugin/version.py
T
ARIA 0f5b5a16ab
CI / Gateway plugin tests (push) Successful in 5m56s
CI / Kotlin tests (android host + desktop) (push) Successful in 7m29s
Fix gateway advertising 'unknown' version in production installs
hermes plugins install <repo>#gateway-plugin ships ONLY the
gateway-plugin/ subdirectory into ~/.hermes/plugins/iris, so the
repo-root VERSION file is not present there and plugin_version()
fell back to 'unknown' — the app then showed a false
'App and gateway versions differ' warning.

- version.py: resolution chain repo-root VERSION (dev/symlink
  install) -> plugin.yaml version field (production install) ->
  'unknown'; stdlib-only parse, base= param for tests
- plugin.yaml: bump stale version 0.1.0 -> 0.1.2 (matches VERSION)
- scripts/check_version_sync.sh + pre-commit hook: fail commits
  where plugin.yaml drifts from the repo-root VERSION
- tests: regression test simulating the production layout
2026-08-27 09:33:31 +02:00

68 lines
2.2 KiB
Python

"""Version discovery.
The repo-root ``VERSION`` file is the single source of truth for the release
version ("everything from here on out is vX.Y.Z" = bump ``VERSION`` and
commit). It is advertised to the app in ``hello.ack``
(``server_caps.app_version``) so the app can show which gateway version it is
talking to.
Resolution order (first hit wins):
1. ``<repo>/VERSION`` — dev checkout / symlink install: the plugin lives at
``<repo>/gateway-plugin``, so the ``VERSION`` file is one directory up.
2. ``version:`` in the plugin's own ``plugin.yaml`` — production install:
``hermes plugins install <repo>#gateway-plugin`` moves ONLY the
``gateway-plugin/`` subdirectory into ``~/.hermes/plugins/iris``, so the
repo-root ``VERSION`` is not present there. ``plugin.yaml`` ships with the
plugin dir; a pre-commit hook (``scripts/check_version_sync.sh``) keeps its
``version:`` field in sync with the repo-root ``VERSION``.
3. ``"unknown"``.
"""
from __future__ import annotations
import re
from pathlib import Path
_FALLBACK = "unknown"
_VERSION_RE = re.compile(r"^version:\s*[\"']?([^\"'\s]+)")
def _read_version_file(path: Path) -> str:
try:
return path.read_text().strip()
except OSError:
return ""
def _plugin_yaml_version(plugin_dir: Path) -> str:
"""The top-level ``version:`` field of ``plugin.yaml`` (stdlib-only parse)."""
try:
text = (plugin_dir / "plugin.yaml").read_text()
except OSError:
return ""
for line in text.splitlines():
m = _VERSION_RE.match(line)
if m:
return m.group(1)
return ""
def plugin_version(base: Path | None = None) -> str:
"""The release version, or ``"unknown"`` if it cannot be found.
``base`` overrides the plugin directory (tests); by default it is the
directory containing this file.
"""
plugin_dir = base if base is not None else Path(__file__).resolve().parent
# 1. Repo-root VERSION (dev checkout / symlink install).
version = _read_version_file(plugin_dir.parent / "VERSION")
if version:
return version
# 2. plugin.yaml (production install ships only the plugin dir).
version = _plugin_yaml_version(plugin_dir)
if version:
return version
return _FALLBACK