fix(desktop): make locally-built macOS app relaunchable after in-place self-update#36198
Merged
Conversation
…e self-update On macOS the desktop app is built locally and ad-hoc signed (no Developer ID on the user's machine). An ad-hoc bundle has no stable Designated Requirement, so when the self-updater rebuilds it in place with a fresh build (new cdhash) — plus the com.apple.quarantine flag inherited from the downloaded installer process chain — Gatekeeper/LaunchServices treats the changed code as tampering and macOS reports "Hermes is damaged and can't be opened," and the app fails to relaunch. First launch works (fresh registration); the in-place update relaunch is what breaks. Fix: after building the desktop app locally, strip quarantine xattrs and re-apply a clean deep ad-hoc signature (omitting the hardened-runtime flag, which an ad-hoc build can't satisfy). Applied in both build entry points: - hermes_cli/main.py cmd_gui (the `hermes desktop --build-only` path the updater drives) — so the fix ships via `hermes update` (git), no installer re-download needed. - scripts/install.sh install_desktop (first install) for parity. Both are no-ops on non-macOS and when a real signing identity (CSC_LINK / APPLE_SIGNING_IDENTITY) is configured, so signed/notarized builds are untouched.
Contributor
🔎 Lint report:
|
themasio
added a commit
to themasio/hermes-agent
that referenced
this pull request
Jun 1, 2026
…#1) Two failures inherited from the latest upstream sync merge (ae9f745): - test_model_catalog: code lists renamed minimax/minimax-m2.7 -> minimax/minimax-m3 but website/static/api/model-catalog.json was not regenerated. Ran scripts/build_model_catalog.py. - test_gui_command: upstream PR NousResearch#36198 (79f7e7a) added _desktop_macos_relaunchable_fixup() to cmd_gui, which fires two extra subprocess.run calls (xattr + codesign) on darwin between pack and launch, but did not update the gui tests. The two darwin tests supplied only [pack_ok, launch_ok] as side_effect, so the launch call hit StopIteration. Mock the fixup helper in those tests, matching the existing pattern that mocks _run_npm_install_deterministic. Co-authored-by: themasio <hugodamasio@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sea-monsters
pushed a commit
to sea-monsters/hermes-agent
that referenced
this pull request
Jun 1, 2026
The upstream commit 79f7e7a (NousResearch#36198) added _desktop_macos_relaunchable_fixup() which calls subprocess.run twice on macOS for xattr + codesign. The gui command tests mock subprocess.run with a side_effect list that only covers the build and launch calls, but the fixup function consumes additional side_effect values, exhausting the iterator and raising StopIteration. Fix: patch _desktop_macos_relaunchable_fixup() in both affected tests so subprocess.run side_effect values are not consumed by fixup calls.
JoeKowal
pushed a commit
to JoeKowal/hermes-agent
that referenced
this pull request
Jun 4, 2026
…e self-update (NousResearch#36198) On macOS the desktop app is built locally and ad-hoc signed (no Developer ID on the user's machine). An ad-hoc bundle has no stable Designated Requirement, so when the self-updater rebuilds it in place with a fresh build (new cdhash) — plus the com.apple.quarantine flag inherited from the downloaded installer process chain — Gatekeeper/LaunchServices treats the changed code as tampering and macOS reports "Hermes is damaged and can't be opened," and the app fails to relaunch. First launch works (fresh registration); the in-place update relaunch is what breaks. Fix: after building the desktop app locally, strip quarantine xattrs and re-apply a clean deep ad-hoc signature (omitting the hardened-runtime flag, which an ad-hoc build can't satisfy). Applied in both build entry points: - hermes_cli/main.py cmd_gui (the `hermes desktop --build-only` path the updater drives) — so the fix ships via `hermes update` (git), no installer re-download needed. - scripts/install.sh install_desktop (first install) for parity. Both are no-ops on non-macOS and when a real signing identity (CSC_LINK / APPLE_SIGNING_IDENTITY) is configured, so signed/notarized builds are untouched.
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On macOS the desktop app is built locally and ad-hoc signed (users have no Developer ID). An ad-hoc bundle has no stable Designated Requirement (no Team ID), so when the self-updater rebuilds it in place with a fresh build (new cdhash) — plus the
com.apple.quarantineflag inherited from the downloaded installer's process chain — Gatekeeper/LaunchServices treats the changed code as tampering and macOS reports "Hermes is damaged and can't be opened," then fails to relaunch.Repro (fresh Mac, happy path): download the signed/notarized thin installer → install (builds
Hermes.applocally) → first launch works → click Update in-app → "Hermes is damaged," app does not reopen. First launch works because it's a fresh registration; the in-place relaunch is what breaks.Fix
After building the desktop app locally, strip quarantine xattrs and re-apply a clean deep ad-hoc signature (omitting the hardened-runtime flag an ad-hoc build can't satisfy), so the rebuilt bundle relaunches.
Applied in both build entry points so it ships via
hermes update(git) with no installer re-download:hermes_cli/main.pycmd_gui— thehermes desktop --build-onlypath the updater drives.scripts/install.shinstall_desktop— first install, for parity.Both are no-ops on non-macOS and when a real signing identity (
CSC_LINK/APPLE_SIGNING_IDENTITY) is configured, so signed/notarized builds are never clobbered.Why this is the right layer
The signed+notarized thin installer (
Hermes Setup) is the downloaded artifact and is unaffected. The big ElectronHermes.appis always built on the user's machine (ad-hoc), so making the locally built app relaunch-safe is the correct fix — and putting it in the build path means existing installs self-heal on their nexthermes update.Test plan
python -c "import ast; ast.parse(...)"onmain.py,bash -n scripts/install.shpass.flags=0x10002(adhoc,runtime)and that an in-place ad-hoc rebuild is what trips the "damaged" check.