fix(update): rebuild Desktop for an installed app with no checkout build
`hermes update` only rebuilt Desktop when apps/desktop/release/ or dist/ existed, so an installed Hermes.app that only the checkout update refreshes (a bootstrap build) never got newer once release/ was gone or never built. Count such bundles as a Desktop to keep current. Ownership is read from the bundle's install-stamp.json: `updateMechanism: self` (or a stamp older than the field). Self-updating releases and commit builds are never rebuilt or copied over, and only the checkout an installed app actually runs (the one under the default Hermes home) claims it. The post-build install now uses the same set.
This commit is contained in:
committed by
brooklyn!
parent
a7c080ca66
commit
53a785449f
@@ -871,6 +871,7 @@ from hermes_cli.main_desktop import ( # frozen updater surface: update_cmd*.py
|
||||
_desktop_macos_relaunchable_fixup,
|
||||
_desktop_packaged_executable,
|
||||
_install_rebuilt_desktop_app,
|
||||
_installed_desktop_apps,
|
||||
)
|
||||
from hermes_cli.main_web_build import (
|
||||
_sweep_stale_bytecode_if_checkout_changed,
|
||||
|
||||
@@ -8,6 +8,7 @@ import logging
|
||||
import contextlib
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
@@ -947,10 +948,44 @@ def _install_rebuilt_desktop_app(desktop_dir: Path) -> tuple[list[Path], list[st
|
||||
rebuilt_exe = _desktop_packaged_executable(desktop_dir)
|
||||
if rebuilt_exe is None:
|
||||
return [], []
|
||||
from hermes_cli.gui_uninstall import packaged_gui_app_paths # noqa: PLC0415
|
||||
# .../Hermes.app/Contents/MacOS/Hermes -> .../Hermes.app
|
||||
return _install_rebuilt_macos_bundles(
|
||||
rebuilt_exe.parents[2], packaged_gui_app_paths(), running=_running_macos_app_bundles())
|
||||
rebuilt_exe.parents[2], _installed_desktop_apps(), running=_running_macos_app_bundles())
|
||||
|
||||
|
||||
def _update_owned_macos_bundles(candidates: list[Path]) -> list[Path]:
|
||||
"""The existing bundles in *candidates* that only ``hermes update`` keeps current (#52339).
|
||||
|
||||
Ownership comes from the bundle's own ``install-stamp.json``. ``updateMechanism: self`` is a
|
||||
bootstrap build (a local pack or the bootstrap download), and stamps older than the field
|
||||
predate every self-updating kind. Bundled/light releases update themselves and commit builds
|
||||
are external, so a local build must never be copied over them. No readable stamp, no claim.
|
||||
"""
|
||||
owned = []
|
||||
for app in candidates:
|
||||
try:
|
||||
stamp = json.loads((app / "Contents" / "Resources" / "install-stamp.json").read_text(encoding="utf-8-sig"))
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
if isinstance(stamp, dict) and stamp.get("updateMechanism", "self") == "self":
|
||||
owned.append(app)
|
||||
return owned
|
||||
|
||||
|
||||
def _installed_desktop_apps() -> list[Path]:
|
||||
"""Installed macOS ``Hermes.app`` bundles this checkout's update owns (none off macOS).
|
||||
|
||||
A packaged app runs the checkout under the default Hermes home, so only that checkout may
|
||||
build for it: a bundle from any other tree (a dev worktree) would split shell from backend.
|
||||
"""
|
||||
if sys.platform != "darwin":
|
||||
return []
|
||||
from hermes_cli.gui_uninstall import packaged_gui_app_paths # noqa: PLC0415
|
||||
from hermes_cli.main import PROJECT_ROOT # noqa: PLC0415
|
||||
from hermes_constants import get_default_hermes_root # noqa: PLC0415
|
||||
if Path(PROJECT_ROOT).resolve() != (get_default_hermes_root() / "hermes-agent").resolve():
|
||||
return []
|
||||
return _update_owned_macos_bundles(packaged_gui_app_paths())
|
||||
|
||||
|
||||
def _install_rebuilt_macos_bundles(
|
||||
|
||||
@@ -1286,9 +1286,12 @@ def _cmd_update_impl(args, gateway_mode: bool):
|
||||
|
||||
|
||||
desktop_dir = _m().PROJECT_ROOT / "apps" / "desktop"
|
||||
# An installed Hermes.app only this update refreshes counts even with no release/ build
|
||||
# beside it: without one it was never rebuilt, so it never got newer (#52339).
|
||||
had_desktop_app_before_update = (
|
||||
_m()._desktop_packaged_executable(desktop_dir) is not None
|
||||
or _m()._desktop_dist_exists(desktop_dir))
|
||||
or _m()._desktop_dist_exists(desktop_dir)
|
||||
or bool(_m()._installed_desktop_apps()))
|
||||
|
||||
use_zip_update, git_cmd, is_fork = _prepare_git_command()
|
||||
|
||||
|
||||
@@ -313,3 +313,28 @@ def test_venv_layout_explicit_and_native(tmp_path, windows, folder, executable):
|
||||
assert venv_python(str(tmp_path), windows=windows) == tmp_path / folder / executable
|
||||
if windows == (os.name == "nt"):
|
||||
assert venv_python(tmp_path) == tmp_path / folder / executable
|
||||
|
||||
|
||||
@pytest.mark.platforms("macos")
|
||||
@pytest.mark.parametrize(("mechanism", "rebuilt"), [("self", True), ("electron-updater", False)])
|
||||
def test_installed_app_without_a_checkout_build_is_still_rebuilt(zip_update, monkeypatch, tmp_path, mechanism, rebuilt):
|
||||
"""#52339: an installed Hermes.app only ``hermes update`` refreshes needs a Desktop build even
|
||||
when release/ is gone, or it never gets newer. A self-updating release is not ours to rebuild."""
|
||||
installed = tmp_path / "Applications" / "Hermes.app"
|
||||
(installed / "Contents" / "Resources").mkdir(parents=True)
|
||||
(installed / "Contents" / "Resources" / "install-stamp.json").write_text(
|
||||
json.dumps({"updateMechanism": mechanism}), encoding="utf-8")
|
||||
monkeypatch.setattr("hermes_cli.gui_uninstall.packaged_gui_app_paths", lambda: [installed])
|
||||
# The checkout under the default Hermes home is the one an installed app runs.
|
||||
(tmp_path / "default-home").mkdir()
|
||||
(tmp_path / "default-home" / "hermes-agent").symlink_to(zip_update.root)
|
||||
monkeypatch.setattr("hermes_constants.get_default_hermes_root", lambda **kw: tmp_path / "default-home")
|
||||
built = []
|
||||
monkeypatch.setattr("hermes_cli.source_build.build_update_products",
|
||||
lambda selected, *, desktop: built.append(desktop))
|
||||
monkeypatch.setattr(update_cmd, "_prepare_git_command", lambda: (True, ["git"], False))
|
||||
monkeypatch.setattr(main, "_warn_orphaned_update_autostashes", lambda *args: None)
|
||||
|
||||
update_cmd._cmd_update_impl(SimpleNamespace(branch="main", yes=True), False)
|
||||
|
||||
assert built == [rebuilt]
|
||||
|
||||
Reference in New Issue
Block a user