fix(dashboard): drop _HERMES_GATEWAY when spawning hermes actions (#52482)
The web dashboard runs inside the gateway process, so `os.environ` carries `_HERMES_GATEWAY=1`. `_spawn_hermes_action` spread that into the subprocess env, so a spawned `hermes gateway restart` (dashboard "Enable webhooks", Telegram QR apply) tripped the in-process restart-loop guard and exited 1 — the gateway never restarted, but the dashboard reported `restart_started: true` because it only checks that the spawn succeeded. Scrub `_HERMES_GATEWAY` from the spawned action's env, matching what the gateway's own restart watcher already does (gateway/run.py). Fixes #52470. Adds a test asserting the spawned env drops the loop-guard var while keeping HERMES_NONINTERACTIVE.
This commit is contained in:
parent
d4c3f98140
commit
bd00212337
2 changed files with 41 additions and 1 deletions
|
|
@ -3282,12 +3282,20 @@ def _spawn_hermes_action(subcommand: List[str], name: str) -> subprocess.Popen:
|
|||
|
||||
cmd = [_dashboard_spawn_executable(), "-m", "hermes_cli.main", *subcommand]
|
||||
|
||||
# The dashboard runs *inside* the gateway process, so os.environ carries
|
||||
# _HERMES_GATEWAY=1. Inheriting it makes a spawned `hermes gateway restart`
|
||||
# trip the in-process restart-loop guard and exit 1 — silently failing the
|
||||
# dashboard's auto-restart paths. The gateway's own restart watcher already
|
||||
# drops it (gateway/run.py); mirror that here (#52470).
|
||||
action_env = {**os.environ, "HERMES_NONINTERACTIVE": "1"}
|
||||
action_env.pop("_HERMES_GATEWAY", None)
|
||||
|
||||
popen_kwargs: Dict[str, Any] = {
|
||||
"cwd": str(PROJECT_ROOT),
|
||||
"stdin": subprocess.DEVNULL,
|
||||
"stdout": log_file,
|
||||
"stderr": subprocess.STDOUT,
|
||||
"env": {**os.environ, "HERMES_NONINTERACTIVE": "1"},
|
||||
"env": action_env,
|
||||
}
|
||||
if sys.platform == "win32":
|
||||
popen_kwargs["creationflags"] = windows_detach_flags()
|
||||
|
|
|
|||
|
|
@ -1283,3 +1283,35 @@ class TestToolsConfigEndpoints:
|
|||
kwargs["json"] = payload
|
||||
r = fn(path, **kwargs)
|
||||
assert r.status_code == 401, f"{method} {path} not gated"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _spawn_hermes_action env scrubbing (#52470)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_spawn_hermes_action_scrubs_gateway_loop_guard_env(monkeypatch, tmp_path):
|
||||
"""The dashboard runs inside the gateway, so os.environ has
|
||||
_HERMES_GATEWAY=1. Spawned actions (e.g. `gateway restart`) must NOT inherit
|
||||
it, or the in-process restart-loop guard rejects the restart and it silently
|
||||
fails (#52470).
|
||||
"""
|
||||
import hermes_cli.web_server as ws
|
||||
|
||||
monkeypatch.setenv("_HERMES_GATEWAY", "1")
|
||||
monkeypatch.setattr(ws, "_ACTION_LOG_DIR", tmp_path)
|
||||
|
||||
captured = {}
|
||||
|
||||
class _FakeProc:
|
||||
pid = 1234
|
||||
|
||||
def _fake_popen(cmd, **kwargs):
|
||||
captured["env"] = kwargs.get("env")
|
||||
return _FakeProc()
|
||||
|
||||
monkeypatch.setattr(ws.subprocess, "Popen", _fake_popen)
|
||||
|
||||
ws._spawn_hermes_action(["gateway", "restart"], "gateway-restart")
|
||||
|
||||
assert "_HERMES_GATEWAY" not in captured["env"]
|
||||
assert captured["env"]["HERMES_NONINTERACTIVE"] == "1"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue