Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion hermes_cli/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,15 @@ def generate_launchd_plist() -> str:
<key>SuccessfulExit</key>
<false/>
</dict>


<!-- Cap the restart delay so the gateway recovers quickly after
`hermes update` triggers a service restart (exit code {GATEWAY_SERVICE_RESTART_EXIT_CODE}).
Without this, launchd's default exponential back-off can delay
restarts by minutes when the process exits repeatedly in quick
succession (e.g. update → restart → brief crash → restart). -->
<key>ThrottleInterval</key>
<integer>10</integer>

<key>StandardOutPath</key>
<string>{log_dir}/gateway.log</string>

Expand Down
19 changes: 19 additions & 0 deletions tests/hermes_cli/test_update_gateway_restart.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,25 @@ def test_plist_program_arguments_order(self):
assert replace_idx == run_idx + 1


class TestLaunchdPlistThrottle:
"""ThrottleInterval must be present so launchd restarts the gateway
quickly after ``hermes update`` triggers a service restart."""

def test_plist_contains_throttle_interval(self):
plist = gateway_cli.generate_launchd_plist()
assert "<key>ThrottleInterval</key>" in plist

def test_plist_throttle_interval_is_10_seconds(self):
plist = gateway_cli.generate_launchd_plist()
lines = [line.strip() for line in plist.splitlines()]
for i, line in enumerate(lines):
if line == "<key>ThrottleInterval</key>":
assert lines[i + 1] == "<integer>10</integer>"
break
else:
raise AssertionError("ThrottleInterval key not found in plist")


class TestLaunchdPlistPath:
def test_plist_contains_environment_variables(self):
plist = gateway_cli.generate_launchd_plist()
Expand Down