fix(bootstrap-installer): stamp setup app version from release semver

Fixes #107177

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
KoNit-K
2026-09-10 16:13:35 +08:00
committed by jonny
parent 04dd80a977
commit 0b8daf30aa
5 changed files with 202 additions and 4 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "@hermes/bootstrap-installer",
"private": true,
"version": "0.0.1",
"version": "0.21.1",
"description": "Hermes Setup — signed installer that drives scripts/install.ps1 with a polished native UI.",
"type": "module",
"scripts": {

View File

@@ -1,6 +1,6 @@
[package]
name = "hermes-bootstrap"
version = "0.0.1"
version = "0.21.1"
description = "Hermes Setup — signed installer that drives scripts/install.ps1"
authors = ["Nous Research <info@nousresearch.com>"]
edition = "2021"

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Hermes",
"version": "0.0.1",
"version": "0.21.1",
"identifier": "com.nousresearch.hermes.setup",
"build": {
"beforeDevCommand": "npm run dev",

View File

@@ -2230,6 +2230,60 @@ def update_version_files(semver: str, calver_date: str):
)
desktop_pkg.write_text(pkg_text, encoding="utf-8")
# Keep the bootstrap installer (Hermes-Setup.dmg CFBundleShortVersionString)
# in lockstep with the Python package version. Tauri reads `version` from
# package.json + tauri.conf.json; a hardcoded 0.0.1 ships in the DMG.
installer_pkg = REPO_ROOT / "apps" / "bootstrap-installer" / "package.json"
if installer_pkg.exists():
pkg_text = installer_pkg.read_text(encoding="utf-8")
pkg_text = re.sub(
r'("version"\s*:\s*)"[^"]+"',
rf'\g<1>"{semver}"',
pkg_text,
count=1,
)
installer_pkg.write_text(pkg_text, encoding="utf-8")
installer_tauri = (
REPO_ROOT / "apps" / "bootstrap-installer" / "src-tauri" / "tauri.conf.json"
)
if installer_tauri.exists():
pkg_text = installer_tauri.read_text(encoding="utf-8")
pkg_text = re.sub(
r'("version"\s*:\s*)"[^"]+"',
rf'\g<1>"{semver}"',
pkg_text,
count=1,
)
installer_tauri.write_text(pkg_text, encoding="utf-8")
installer_cargo = (
REPO_ROOT / "apps" / "bootstrap-installer" / "src-tauri" / "Cargo.toml"
)
if installer_cargo.exists():
cargo_text = installer_cargo.read_text(encoding="utf-8")
cargo_text = re.sub(
r'^version\s*=\s*"[^"]+"',
f'version = "{semver}"',
cargo_text,
count=1,
flags=re.MULTILINE,
)
installer_cargo.write_text(cargo_text, encoding="utf-8")
def version_files_to_stage() -> list[str]:
"""Return version-bearing files that exist and should be `git add`ed after a bump."""
candidates = [
VERSION_FILE,
PYPROJECT_FILE,
REPO_ROOT / "apps" / "desktop" / "package.json",
REPO_ROOT / "apps" / "bootstrap-installer" / "package.json",
REPO_ROOT / "apps" / "bootstrap-installer" / "src-tauri" / "tauri.conf.json",
REPO_ROOT / "apps" / "bootstrap-installer" / "src-tauri" / "Cargo.toml",
]
return [str(path) for path in candidates if path.exists()]
def resolve_author(name: str, email: str) -> str:
"""Resolve a git author to a GitHub @mention."""
@@ -2568,7 +2622,7 @@ def main():
print(f" ✓ Updated version files to v{new_version} ({calver_date})")
# Commit version bump
add_files = [str(VERSION_FILE), str(PYPROJECT_FILE)]
add_files = version_files_to_stage()
add_result = git_result("add", *add_files)
if add_result.returncode != 0:
print(f" ✗ Failed to stage version files: {add_result.stderr.strip()}")

View File

@@ -0,0 +1,144 @@
"""release.py must stamp bootstrap-installer versions with the release semver.
Tauri CFBundleShortVersionString is read from
apps/bootstrap-installer/src-tauri/tauri.conf.json (and the sibling
package.json). Those files were hardcoded 0.0.1 and omitted from
update_version_files / the --publish --bump git add list, so Hermes-Setup.dmg
always shipped 0.0.1. Same class as the desktop stamp (#68783 / PR #68796).
"""
from __future__ import annotations
import importlib.util
import json
from pathlib import Path
SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "release.py"
def _load():
spec = importlib.util.spec_from_file_location(
"release_bootstrap_installer_version", SCRIPT
)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
release = _load()
def _json_version(path: Path) -> str:
return json.loads(path.read_text(encoding="utf-8"))["version"]
def _patch_repo(tmp_path, monkeypatch, *, with_installer: bool = True):
repo = tmp_path
init_py = repo / "hermes_cli" / "__init__.py"
init_py.parent.mkdir(parents=True)
init_py.write_text(
'__version__ = "0.0.1"\n__release_date__ = "2026.1.1"\n',
encoding="utf-8",
)
pyproject = repo / "pyproject.toml"
pyproject.write_text('version = "0.0.1"\n', encoding="utf-8")
desktop_pkg = repo / "apps" / "desktop" / "package.json"
desktop_pkg.parent.mkdir(parents=True)
desktop_pkg.write_text('{"version":"0.0.1"}\n', encoding="utf-8")
installer_pkg = repo / "apps" / "bootstrap-installer" / "package.json"
tauri_conf = (
repo / "apps" / "bootstrap-installer" / "src-tauri" / "tauri.conf.json"
)
cargo_toml = repo / "apps" / "bootstrap-installer" / "src-tauri" / "Cargo.toml"
if with_installer:
tauri_conf.parent.mkdir(parents=True)
installer_pkg.write_text(
'{"name":"x","version":"0.0.1"}\n', encoding="utf-8"
)
tauri_conf.write_text(
'{"productName":"Hermes","version":"0.0.1"}\n', encoding="utf-8"
)
cargo_toml.write_text('[package]\nversion = "0.0.1"\n', encoding="utf-8")
monkeypatch.setattr(release, "REPO_ROOT", repo)
monkeypatch.setattr(release, "VERSION_FILE", init_py)
monkeypatch.setattr(release, "PYPROJECT_FILE", pyproject)
return {
"repo": repo,
"init_py": init_py,
"pyproject": pyproject,
"desktop_pkg": desktop_pkg,
"installer_pkg": installer_pkg,
"tauri_conf": tauri_conf,
"cargo_toml": cargo_toml,
}
def test_update_version_files_stamps_bootstrap_installer(tmp_path, monkeypatch):
paths = _patch_repo(tmp_path, monkeypatch)
release.update_version_files("0.21.1", "2026.9.10")
assert _json_version(paths["installer_pkg"]) == "0.21.1"
assert _json_version(paths["tauri_conf"]) == "0.21.1"
assert 'version = "0.21.1"' in paths["cargo_toml"].read_text(encoding="utf-8")
# CONTROL: existing desktop / Python stamps still happen.
assert _json_version(paths["desktop_pkg"]) == "0.21.1"
assert 'version = "0.21.1"' in paths["pyproject"].read_text(encoding="utf-8")
init_text = paths["init_py"].read_text(encoding="utf-8")
assert '__version__ = "0.21.1"' in init_text
assert '__release_date__ = "2026.9.10"' in init_text
def test_update_version_files_skips_missing_installer_dir(tmp_path, monkeypatch):
paths = _patch_repo(tmp_path, monkeypatch, with_installer=False)
release.update_version_files("0.21.1", "2026.9.10")
assert not paths["installer_pkg"].exists()
assert not paths["tauri_conf"].exists()
assert _json_version(paths["desktop_pkg"]) == "0.21.1"
assert 'version = "0.21.1"' in paths["pyproject"].read_text(encoding="utf-8")
assert '__version__ = "0.21.1"' in paths["init_py"].read_text(encoding="utf-8")
def test_update_version_files_does_not_invent_version_keys(tmp_path, monkeypatch):
paths = _patch_repo(tmp_path, monkeypatch)
original = '{"name":"x","productName":"Hermes"}\n'
paths["installer_pkg"].write_text(original, encoding="utf-8")
paths["tauri_conf"].write_text(original, encoding="utf-8")
release.update_version_files("0.21.1", "2026.9.10")
assert paths["installer_pkg"].read_text(encoding="utf-8") == original
assert paths["tauri_conf"].read_text(encoding="utf-8") == original
assert _json_version(paths["desktop_pkg"]) == "0.21.1"
def test_version_files_to_stage_includes_installer_when_present(tmp_path, monkeypatch):
paths = _patch_repo(tmp_path, monkeypatch)
staged = release.version_files_to_stage()
assert str(paths["init_py"]) in staged
assert str(paths["pyproject"]) in staged
assert str(paths["desktop_pkg"]) in staged
assert str(paths["installer_pkg"]) in staged
assert str(paths["tauri_conf"]) in staged
assert str(paths["cargo_toml"]) in staged
def test_version_files_to_stage_omits_missing_installer(tmp_path, monkeypatch):
paths = _patch_repo(tmp_path, monkeypatch, with_installer=False)
staged = release.version_files_to_stage()
assert str(paths["installer_pkg"]) not in staged
assert str(paths["tauri_conf"]) not in staged
assert str(paths["cargo_toml"]) not in staged
assert str(paths["desktop_pkg"]) in staged
assert str(paths["init_py"]) in staged
assert str(paths["pyproject"]) in staged