From f7289b3c2421fc2ad003b301cdc7c1952e26e5f5 Mon Sep 17 00:00:00 2001 From: PRATHAMESH75 Date: Sat, 25 Jul 2026 05:12:27 +0530 Subject: [PATCH] fix(release): stage the desktop package.json bump so it lands in the release commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update_version_files() writes apps/desktop/package.json in lockstep with pyproject, but main() only staged VERSION_FILE and PYPROJECT_FILE — so the desktop bump was written to the tree and then silently dropped from the release commit. Built desktop apps therefore reported a stale CFBundleShortVersionString (stuck at 0.17.0 through v0.18.x/v0.19.0). Make update_version_files() return the exact set of files it wrote and have main() stage that set, so a newly-bumped file can't drift out of the commit again. Also correct the currently-stale apps/desktop/package.json to 0.19.0 to match hermes_cli.__version__. Fixes #68783 --- apps/desktop/package.json | 2 +- scripts/release.py | 24 +++++-- tests/scripts/test_release_version_files.py | 72 +++++++++++++++++++++ 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 tests/scripts/test_release_version_files.py diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 0d2c07756af8..789475b1e189 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -2,7 +2,7 @@ "name": "hermes", "productName": "Hermes", "private": true, - "version": "0.17.0", + "version": "0.19.0", "description": "Native desktop shell for Hermes Agent.", "author": "Nous Research", "type": "module", diff --git a/scripts/release.py b/scripts/release.py index 0df1a1b70d57..5046c03ece4a 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -2165,8 +2165,15 @@ def bump_version(current: str, part: str) -> str: return f"{major}.{minor}.{patch}" -def update_version_files(semver: str, calver_date: str): - """Update version strings in source files.""" +def update_version_files(semver: str, calver_date: str) -> list[Path]: + """Update version strings in source files. + + Returns the exact set of files that were written so the caller can stage + precisely what changed. A newly-bumped file that is written but not + returned here would be silently dropped from the release commit (#68783). + """ + modified: list[Path] = [] + # Update __init__.py content = VERSION_FILE.read_text(encoding="utf-8") content = re.sub( @@ -2180,6 +2187,7 @@ def update_version_files(semver: str, calver_date: str): content, ) VERSION_FILE.write_text(content, encoding="utf-8") + modified.append(VERSION_FILE) # Update pyproject.toml pyproject = PYPROJECT_FILE.read_text(encoding="utf-8") @@ -2190,6 +2198,7 @@ def update_version_files(semver: str, calver_date: str): flags=re.MULTILINE, ) PYPROJECT_FILE.write_text(pyproject, encoding="utf-8") + modified.append(PYPROJECT_FILE) # Keep the desktop Electron app's package.json version in lockstep with the # Python package version. The desktop About panel reads the live Hermes @@ -2205,6 +2214,9 @@ def update_version_files(semver: str, calver_date: str): count=1, ) desktop_pkg.write_text(pkg_text, encoding="utf-8") + modified.append(desktop_pkg) + + return modified def resolve_author(name: str, email: str) -> str: @@ -2540,11 +2552,13 @@ def main(): # Update version files if args.bump: - update_version_files(new_version, calver_date) + written = update_version_files(new_version, calver_date) print(f" ✓ Updated version files to v{new_version} ({calver_date})") - # Commit version bump - add_files = [str(VERSION_FILE), str(PYPROJECT_FILE)] + # Commit version bump. Stage exactly the files update_version_files + # wrote, so a newly-bumped file (e.g. the desktop package.json) can't + # drift out of the release commit (#68783). + add_files = [str(p) for p in written] add_result = git_result("add", *add_files) if add_result.returncode != 0: print(f" ✗ Failed to stage version files: {add_result.stderr.strip()}") diff --git a/tests/scripts/test_release_version_files.py b/tests/scripts/test_release_version_files.py new file mode 100644 index 000000000000..2d48f1d4a57d --- /dev/null +++ b/tests/scripts/test_release_version_files.py @@ -0,0 +1,72 @@ +"""Tests for ``release.update_version_files`` write/return contract. + +The release commit stages exactly the files ``update_version_files`` reports +back. A file that gets written but not returned is silently dropped from the +commit — which is how the desktop ``package.json`` bump went stale for several +releases (#68783). These tests pin the contract that every file the function +writes is also returned. +""" + +import json +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] +SCRIPTS_DIR = REPO_ROOT / "scripts" + +sys.path.insert(0, str(SCRIPTS_DIR)) + +import release # noqa: E402 + + +def _prepare_tree(monkeypatch, root: Path, *, with_desktop: bool) -> None: + """Pin release's module-level path constants at a throwaway temp tree so a + bump never scribbles on the real repo files.""" + (root / "pyproject.toml").write_text( + '[project]\nname = "hermes-agent"\nversion = "0.13.0"\n', encoding="utf-8" + ) + version_dir = root / "hermes_cli" + version_dir.mkdir() + (version_dir / "__init__.py").write_text( + '__version__ = "0.13.0"\n__release_date__ = "2026-05-14"\n', + encoding="utf-8", + ) + monkeypatch.setattr(release, "VERSION_FILE", version_dir / "__init__.py") + monkeypatch.setattr(release, "PYPROJECT_FILE", root / "pyproject.toml") + monkeypatch.setattr(release, "REPO_ROOT", root) + + if with_desktop: + pkg_dir = root / "apps" / "desktop" + pkg_dir.mkdir(parents=True) + (pkg_dir / "package.json").write_text( + json.dumps({"name": "hermes", "version": "0.13.0"}, indent=2) + "\n", + encoding="utf-8", + ) + + +def test_returns_desktop_package_when_present(monkeypatch, tmp_path): + """When the desktop package.json exists it must be both bumped AND returned + so the release commit can stage it (#68783).""" + _prepare_tree(monkeypatch, tmp_path, with_desktop=True) + + modified = release.update_version_files("0.14.0", "2026-05-21") + + desktop_pkg = tmp_path / "apps" / "desktop" / "package.json" + assert desktop_pkg in modified, "desktop package.json written but not returned" + # The bump actually landed on disk. + assert json.loads(desktop_pkg.read_text())["version"] == "0.14.0" + # Core version files are always reported. + assert release.VERSION_FILE in modified + assert release.PYPROJECT_FILE in modified + + +def test_omits_desktop_package_when_absent(monkeypatch, tmp_path): + """Older release branches predate the desktop app — no package.json means + it must not be reported (staging a missing path would abort the release).""" + _prepare_tree(monkeypatch, tmp_path, with_desktop=False) + + modified = release.update_version_files("0.14.0", "2026-05-21") + + assert all("package.json" not in str(p) for p in modified) + assert release.VERSION_FILE in modified + assert release.PYPROJECT_FILE in modified