From cb2c13055ed9c3f467be61dbef1f3a7a5dcdc5f6 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Wed, 10 Jun 2026 20:54:32 -0700 Subject: [PATCH] fix(gateway): scrub _HERMES_GATEWAY from POSIX detached restart watcher too Follow-up to the salvaged #41264 (Windows watcher): the setsid/bash detached restart watcher on Linux/macOS inherits _HERMES_GATEWAY=1 the same way, so the CLI's self-restart loop guard silently refuses 'hermes gateway restart' and the gateway never comes back. Scrub the marker from the watcher env on the POSIX branch as well, and extend the setsid test to assert it. --- gateway/run.py | 9 +++++++++ scripts/release.py | 1 + tests/gateway/test_restart_drain.py | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/gateway/run.py b/gateway/run.py index ff82fe43582..897cb85f652 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -4338,12 +4338,20 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew f"while kill -0 {current_pid} 2>/dev/null; do sleep 0.2; done; " f"{cmd} gateway restart" ) + # Same marker scrub as the Windows watcher above: this watcher runs + # `hermes gateway restart` from outside the gateway, but it inherits + # _HERMES_GATEWAY=1 from us, and the CLI's self-restart loop guard + # refuses to run when that marker is set — silently (DEVNULL), so the + # gateway stops and never comes back. + watcher_env = os.environ.copy() + watcher_env.pop("_HERMES_GATEWAY", None) setsid_bin = shutil.which("setsid") if setsid_bin: subprocess.Popen( [setsid_bin, "bash", "-lc", shell_cmd], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + env=watcher_env, start_new_session=True, ) else: @@ -4351,6 +4359,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew ["bash", "-lc", shell_cmd], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + env=watcher_env, start_new_session=True, ) diff --git a/scripts/release.py b/scripts/release.py index 16b8bd7cdb8..ff4f4bc6da7 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -75,6 +75,7 @@ AUTHOR_MAP = { "129007007+HeLLGURD@users.noreply.github.com": "HeLLGURD", "290859878+synapsesx@users.noreply.github.com": "synapsesx", "dirtyren@users.noreply.github.com": "dirtyren", + "470766206@qq.com": "youjunxiaji", "mharris@parallel.ai": "NormallyGaussian", "roger@roger.local": "mollusk", "ted.malone@outlook.com": "temalo", diff --git a/tests/gateway/test_restart_drain.py b/tests/gateway/test_restart_drain.py index 32710fdb897..15b948a4f79 100644 --- a/tests/gateway/test_restart_drain.py +++ b/tests/gateway/test_restart_drain.py @@ -200,6 +200,7 @@ async def test_launch_detached_restart_command_uses_setsid(monkeypatch): monkeypatch.setattr(gateway_run.sys, "platform", "linux") monkeypatch.setattr(gateway_run, "_resolve_hermes_bin", lambda: ["/usr/bin/hermes"]) monkeypatch.setattr(gateway_run.os, "getpid", lambda: 321) + monkeypatch.setenv("_HERMES_GATEWAY", "1") monkeypatch.setattr(shutil, "which", lambda cmd: "/usr/bin/setsid" if cmd == "setsid" else None) def fake_popen(cmd, **kwargs): @@ -218,6 +219,9 @@ async def test_launch_detached_restart_command_uses_setsid(monkeypatch): assert kwargs["start_new_session"] is True assert kwargs["stdout"] is subprocess.DEVNULL assert kwargs["stderr"] is subprocess.DEVNULL + # The watcher must NOT inherit the gateway marker, or the CLI's + # self-restart loop guard refuses to run `hermes gateway restart`. + assert kwargs["env"].get("_HERMES_GATEWAY") is None def test_windows_gateway_venv_imports_add_site_packages(monkeypatch, tmp_path):