fix(update): refresh stale launchd plists and restart all profile gateways on macOS#38627
Open
joelbrilliant1-beep wants to merge 1 commit into
Open
Conversation
…eways on macOS `hermes update` on macOS left gateways on stale launchd service definitions and only ever restarted the current profile's LaunchAgent. launchd never hot-reloads a plist (only bootout/bootstrap applies a changed definition), but the update postflight called launchd_restart(), which kickstarts the cached definition, so a service whose plist changed came back running the old one. `hermes gateway start` self-heals via refresh_launchd_plist_if_needed(); the update path did not, forcing a manual `hermes gateway start` per profile. Add restart_launchd_gateways_for_update(): discover every ai.hermes.gateway*.plist, re-enter each plist's recorded HERMES_HOME via the _HERMES_HOME_OVERRIDE ContextVar, and for each loaded gateway either reload a stale definition (draining in-flight runs with SIGUSR1 first) or graceful-restart a current one. The macOS postflight now delegates to it, so named-profile gateways are handled too. launchd_restart() is unchanged, preserving its graceful-restart contract. Tests mock launchctl/subprocess throughout, so no live gateway commands run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
What does this PR do?
On macOS,
hermes updateleft running gateways on stale launchd service definitions and only restarted the current profile's LaunchAgent, leaving named-profile gateways pinned to the old code.Two root causes:
bootout/bootstrap. The update postflight calledlaunchd_restart(), whichkickstarts the cached definition, so a service whose plist changed across the update came back running the old one.launchd_start()self-heals viarefresh_launchd_plist_if_needed(); the update path did not, forcing users to manually runhermes gateway start.get_launchd_label()/get_launchd_plist_path(). The Linux/systemd branch right above it already enumerates allhermes-gateway*units; launchd was the outlier.Fix
New
restart_launchd_gateways_for_update()inhermes_cli/gateway.py:~/Library/LaunchAgents/ai.hermes.gateway*.plist(the launchd analogue ofsystemctl list-units hermes-gateway*— all profiles share one LaunchAgents dir).HERMES_HOMEand re-enters that profile's context via the existing_HERMES_HOME_OVERRIDEContextVar, so each plist is regenerated for the correct profile.SIGUSR1first, thenrefresh_launchd_plist_if_needed()does the bootout/bootstrap reload.The macOS update postflight now delegates to it.
launchd_restart()is unchanged, so its existing graceful-restart contract (and tests) are preserved.Related Issue
Fixes #38053
This is distinct from the other open macOS launchd-restart PRs: it specifically addresses multi-profile enumeration and stale-plist refresh in the
hermes updatepostflight, rather than KeepAlive/missing-plist/throttle behaviour inlaunchd_restart()itself (which is left untouched).Type of Change
Changes Made
hermes_cli/gateway.py: add_launchd_agents_dir,_iter_hermes_launchd_plists,_hermes_home_from_launchd_plist,_launchd_service_is_loaded, andrestart_launchd_gateways_for_update(). No existing function is modified (onlyimport plistlibis added).hermes_cli/main.py: replace the single-profile macOS restart branch in_cmd_update_implwith a delegation to a small helper_restart_macos_launchd_gateways().tests/hermes_cli/test_gateway_service.py:TestLaunchdUpdatePostflight(11 tests).tests/hermes_cli/test_cmd_update.py:TestRestartMacosLaunchdGatewaysPostflight(2 tests).How to Test
All side effects (launchctl, signals, subprocess) are mocked — no live gateway commands run.
Coverage includes: stale plist rewritten + bootout/bootstrap reload; a named profile regenerated in its own context (keeps its
--profilearg); unloaded agents skipped; unreadable plists skipped; one profile failing does not abort the rest; and an assertion that every subprocess invocation islaunchctl(proving no livehermescommands are needed).Checklist
Code
fix(update): ...)pytest tests/ -qon macOS has pre-existingsystemd/seed_superviseenv failures unrelated to this change (CI runs the full suite on Linux)Documentation & Housekeeping
docs/— N/Acli-config.yaml.exampleN/ACONTRIBUTING.md/AGENTS.mdN/A🤖 Generated with Claude Code