#!/usr/bin/env python3 """Derive a Debian package version (or channel) from a hermes-agent release tag. Pure function; imported by scripts/termux/build_deb.sh and unit-tested by tests/scripts/test_termux_deb_version.py (Task 4 of .hermes/plans/2026-08-31_termux-deb.md). Mapping: v1.2.3 -> 1.2.3-1 v1.2.3+canary.20260831T120000Z -> 1.2.3~canary.20260831T120000Z-1 The ``~`` ranks the canary below the corresponding stable in dpkg's version ordering. ``--channel`` derives the release channel from the SAME tag regex: a tag with a nightly timestamp is ``nightly``, everything else is ``stable``. This is the single source of truth for the channel; workflows and other tooling must call this instead of re-typing a case statement. """ from __future__ import annotations import re import sys _TAG_RE = re.compile( r"^v(?P0|[1-9]\d{0,2})\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)" r"(?:\+canary\.(?P20\d{6}T\d{6}Z))?$" ) def _match_tag(tag: str) -> re.Match[str]: m = _TAG_RE.match(tag) if m is None: raise ValueError( f"malformed release tag {tag!r}: expected v.. " "or v..+canary." ) return m def deb_version_for_tag(tag: str) -> str: """Map a release tag to its Debian version. Raises ValueError on malformed tags.""" m = _match_tag(tag) base = f"{m.group('major')}.{m.group('minor')}.{m.group('patch')}" ts = m.group("ts") if ts is None: return f"{base}-1" return f"{base}~canary.{ts}-1" def channel_for_tag(tag: str) -> str: """Map a release tag to its channel: 'canary' or 'stable'. Derived from the same _TAG_RE as deb_version_for_tag, so the two can never drift: a tag that yields a '~canary' deb version is canary, and the malformed-tag rejection is identical. """ m = _match_tag(tag) return "canary" if m.group("ts") else "stable" def main(argv: list[str]) -> int: args = argv[1:] channel_mode = False if args and args[0] == "--channel": channel_mode = True args = args[1:] if len(args) != 1: mode = "deb_version.py --channel " if channel_mode else "deb_version.py " print(f"usage: {mode}", file=sys.stderr) return 2 try: print(channel_for_tag(args[0]) if channel_mode else deb_version_for_tag(args[0])) except ValueError as exc: print(f"deb_version: {exc}", file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main(sys.argv))