Skip to content

fix(desktop): make locally-built macOS app relaunchable after in-place self-update#36198

Merged
OutThisLife merged 1 commit into
mainfrom
bb/mac-desktop-update-relaunch
Jun 1, 2026
Merged

fix(desktop): make locally-built macOS app relaunchable after in-place self-update#36198
OutThisLife merged 1 commit into
mainfrom
bb/mac-desktop-update-relaunch

Conversation

@OutThisLife

Copy link
Copy Markdown
Collaborator

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.quarantine flag 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.app locally) → 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.py cmd_gui — the hermes desktop --build-only path the updater drives.
  • 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 never clobbered.

Why this is the right layer

The signed+notarized thin installer (Hermes Setup) is the downloaded artifact and is unaffected. The big Electron Hermes.app is 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 next hermes update.

Test plan

  • python -c "import ast; ast.parse(...)" on main.py, bash -n scripts/install.sh pass.
  • Confirmed the built app is flags=0x10002(adhoc,runtime) and that an in-place ad-hoc rebuild is what trips the "damaged" check.
  • On a fresh Mac: install via the notarized thin installer → click Update in-app → verify it rebuilds, relaunches, and shows no "damaged" dialog.

…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.
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: bb/mac-desktop-update-relaunch vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9556 on HEAD, 9556 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4954 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@OutThisLife OutThisLife merged commit 79f7e7a into main Jun 1, 2026
19 of 21 checks passed
@OutThisLife OutThisLife deleted the bb/mac-desktop-update-relaunch branch June 1, 2026 02:27
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant