From 52329eec8352f8855c3a4515e43379f5ea297733 Mon Sep 17 00:00:00 2001 From: Shreyansh Jain Date: Fri, 11 Sep 2026 15:08:31 +0530 Subject: [PATCH] fix(desktop): stop a stale hand-off root failing a healthy update --- hermes_cli/desktop_update_verify.py | 25 +++++++++++-- .../hermes_cli/test_desktop_update_verify.py | 35 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/hermes_cli/desktop_update_verify.py b/hermes_cli/desktop_update_verify.py index 5bd252bf40..ee9dabec58 100644 --- a/hermes_cli/desktop_update_verify.py +++ b/hermes_cli/desktop_update_verify.py @@ -74,15 +74,34 @@ def checkout_root() -> Path: return Path(__file__).resolve().parent.parent +def _root_to_verify(project_root: Path | None) -> Path: + """The root the receipt may describe: a caller's root is a hint, not a contract. + + The hand-off script is read before the pull, so an update that ships a fix to + the caller side cannot apply it to its own run: a pre-fix ``windows.ps1`` still + passes ``Path.cwd()``, which is HERMES_HOME and never holds a packaged Desktop + app. Honour a caller-supplied root only while it actually carries one, and fall + back to the checkout this module was imported from -- the tree the update just + wrote. A supplied root that does carry a packaged app is still verified as + given, so real damage there stays fail-closed. + """ + if project_root is None: + return checkout_root() + if _desktop_packaged_executable(project_root / "apps" / "desktop") is not None: + return project_root + return checkout_root() + + def verify_windows_desktop_update(project_root: Path | None = None) -> None: """Raise when a zero-exit updater left an incomplete or stale packaged app. The root defaults to the imported checkout, never the caller's cwd: the hand-off is spawned from HERMES_HOME by the pre-update Desktop, and a cwd-derived root - reported a healthy install as "Desktop executable is missing" (Sep 2026). + reported a healthy install as "Desktop executable is missing" (Sep 2026). A + caller-supplied root with no packaged app falls back to that same checkout, so a + stale in-flight hand-off cannot fail a healthy install either. """ - if project_root is None: - project_root = checkout_root() + project_root = _root_to_verify(project_root) desktop = project_root / "apps" / "desktop" executable = _desktop_packaged_executable(desktop) if executable is None: diff --git a/tests/hermes_cli/test_desktop_update_verify.py b/tests/hermes_cli/test_desktop_update_verify.py index 0b8d1aef27..241e52839d 100644 --- a/tests/hermes_cli/test_desktop_update_verify.py +++ b/tests/hermes_cli/test_desktop_update_verify.py @@ -64,3 +64,38 @@ def test_default_root_is_the_imported_checkout_not_cwd(tmp_path, monkeypatch): assert seen['desktop'] == verify.checkout_root() / 'apps' / 'desktop' assert (verify.checkout_root() / 'hermes_cli' / 'desktop_update_verify.py').is_file() assert verify.checkout_root() != tmp_path + + +def _app_only_under(root): + """A packaged-app lookup that finds an app under *root* and nowhere else.""" + from pathlib import Path + + desktop = (root / 'apps' / 'desktop').resolve() + + def lookup(candidate): + return desktop / 'release/fixture/Hermes.exe' if Path(candidate).resolve() == desktop else None + + return lookup + + +def test_caller_root_without_a_packaged_app_falls_back_to_the_checkout(bundle, tmp_path, monkeypatch): + # A hand-off script read before the pull still passes HERMES_HOME, which never + # holds a packaged app. The healthy checkout it just wrote must be verified instead. + checkout, _, _ = bundle + monkeypatch.setattr(verify, '_desktop_packaged_executable', _app_only_under(checkout)) + monkeypatch.setattr(verify, 'checkout_root', lambda: checkout) + verify.verify_windows_desktop_update(tmp_path / 'hermes_home') + + +def test_caller_root_that_has_a_packaged_app_is_verified_as_given(bundle, tmp_path, monkeypatch): + # Fail-closed: the fallback is for a root with nothing to read, never a way to + # launder a damaged build past the receipt by switching to a healthy tree. + checkout, _, _ = bundle + supplied = tmp_path / 'supplied' + resources = supplied / 'apps/desktop/release/fixture/resources' + resources.mkdir(parents=True) + (resources / 'app.asar').write_bytes(b'not an asar') + monkeypatch.setattr(verify, '_desktop_packaged_executable', _app_only_under(supplied)) + monkeypatch.setattr(verify, 'checkout_root', lambda: checkout) + with pytest.raises(RuntimeError, match='archive or main entry is invalid'): + verify.verify_windows_desktop_update(supplied)