Files
hermes-agent/scripts/releases/stamping.py
ethernet c13ea774e6 refactor: make install-stamp.json the single runtime version identity
Runtime identity resolved through hermes_cli.__version__ (a static 0.0.0
on source installs, rewritten by release stamping) leaked v0.0.0 into
About, /api/health, User-Agents, and plugin compat, and source updates
showed "couldn't reach update server" because identity and channel
authority disagreed with the checkout.

Now: get_version_info() resolves install stamp -> live git -> unknown,
never pyproject metadata, never a package constant. Source checkouts
derive identity from their reachable release tag; the completion tail of
every successful install/update/historical takeover atomically rewrites
install-stamp.json with that identity; a stale source stamp whose commit
no longer matches HEAD defers to live git. ACP/TUI use derived_version
for display and base_version for protocol fields; all ~44 runtime
__version__ consumers migrated; hermes_cli.__version__ and generated
_version.py are gone; release stamping only touches the native manifests
external builders consume (nix/tauri/cargo) and passes release identity
straight into write_install_stamp.py; pyproject.toml stays inert 0.0.0.
Desktop no longer synthesizes a competing install-stamp.json: the
checkout owns its stamp, and desktop-bootstrap classification keys on
the bootstrap-complete marker. verify-bootstrap-version-stamp.py now
cross-checks the checkout's stamp (baseVersion + commit == HEAD).

Validation: 31-file focused suite green (version identity, stamping,
adoption, providers, gateway, acp/tui runtime identity, api server via
extras env, release graph); desktop tsc + 25 vitest green; real-repo
probe: base=unknown derived=git.0635606.dirty source=git on this
checkout; clean-env imports resolve entirely from this tree; windows
footgun + compat-pointer scans clean.
2026-09-23 11:41:01 -04:00

88 lines
2.8 KiB
Python

"""Stamp only release metadata consumed by external package builders.
Python and npm package versions are inert source placeholders. Runtime identity
comes from ``install-stamp.json``; this adapter exists only for builders that
require a native version in their own project metadata.
"""
from __future__ import annotations
import argparse
import json
import re
import tomllib
from pathlib import Path
def _rewrite(path: Path, pattern: str, replacement: str, *, count: int = 0, flags: int = 0) -> bool:
if not path.exists():
return False
raw = path.read_bytes()
newline = "\r\n" if b"\r\n" in raw else "\n"
text = raw.decode("utf-8-sig").replace("\r\n", "\n")
updated = re.sub(pattern, replacement, text, count=count, flags=flags)
path.write_bytes(updated.replace("\n", newline).encode("utf-8"))
return True
def stamp(tree: Path, version: str) -> list[Path]:
"""Rewrite external builder version inputs under ``tree``."""
written: list[Path] = []
nix_package = tree / "nix" / "hermes-agent.nix"
if _rewrite(
nix_package,
r'^ version \? "[^"]+",',
f' version ? "{version}",',
count=1,
flags=re.MULTILINE,
):
written.append(nix_package)
installer = tree / "apps" / "bootstrap-installer" / "src-tauri"
json_version = rf'\g<1>"{version}"'
for path, pattern, replacement, flags in (
(
installer / "tauri.conf.json",
r'("version"\s*:\s*)"[^"]+"',
json_version,
0,
),
(
installer / "Cargo.toml",
r'^version\s*=\s*"[^"]+"',
f'version = "{version}"',
re.MULTILINE,
),
):
if _rewrite(path, pattern, replacement, count=1, flags=flags):
written.append(path)
if installer.is_dir():
validate_bootstrap_version(tree, version)
return written
def validate_bootstrap_version(tree: Path, version: str) -> None:
installer = tree / "apps" / "bootstrap-installer" / "src-tauri"
tauri = json.loads((installer / "tauri.conf.json").read_text(encoding="utf-8-sig"))
cargo = tomllib.loads((installer / "Cargo.toml").read_text(encoding="utf-8-sig"))
values = {
"Tauri config": tauri.get("version"),
"Cargo package": cargo.get("package", {}).get("version"),
}
mismatches = {name: value for name, value in values.items() if value != version}
if mismatches:
raise ValueError(f"Bootstrap installer version differs from {version}: {mismatches}")
def main(argv: list[str] | None = None) -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--tree", type=Path, required=True)
parser.add_argument("--version", required=True)
args = parser.parse_args(argv)
stamp(args.tree.resolve(), args.version)
if __name__ == "__main__":
main()