fix(tests): guard against real 'hermes update' subprocess spawns in conftest

Extends _live_system_guard in tests/conftest.py to block any subprocess
call that would run 'hermes update' (or 'python -m hermes_cli.main update')
against the real checkout.

These commands run git fetch origin + git pull, overwriting repo files
like pyproject.toml mid-test-run and corrupting every subsequent
subprocess that reads them. The spawned process uses setsid /
start_new_session=True so it's invisible to pytest's process tree
(PPid=1) — the corruption was essentially undetectable without
explicit inotify/SHA watchdogs.

Root cause of #43703 CI failures: tests in TestUpdateCommandPlatformGate
called _handle_update_command() with HERMES_MANAGED='' and no Popen mock,
causing the code to fall through and spawn a real 'hermes update --gateway'
that overwrote pyproject.toml with origin/main's content (which still
had '--timeout=30 --timeout-method=thread' in addopts while the PR had
already removed pytest-timeout).

The guard covers all three invocation patterns:
- 'hermes update' / 'hermes update --gateway' (direct or via setsid bash -c)
- 'python -m hermes_cli.main update --gateway'
- '.venv/bin/hermes update' (absolute path variant)

Does not false-positive on: git update-index, apt-get update,
pip install --upgrade, or any command lacking 'hermes'/'hermes_cli'.
This commit is contained in:
ethernet 2026-06-12 01:19:36 -04:00
parent c41a6534cf
commit 6ff39c31ad

View file

@ -731,6 +731,41 @@ def _live_system_guard(request, monkeypatch):
"Mark with @pytest.mark.live_system_guard_bypass if "
"intentional."
)
# Block any subprocess that would run `hermes update` (or the
# equivalent `python -m hermes_cli.main update`). These commands
# run `git fetch origin + git pull` against the REAL checkout,
# overwriting files like pyproject.toml mid-test-run and corrupting
# every subsequent subprocess that reads them. The corruption is
# especially insidious because the spawned process uses setsid/
# start_new_session=True, making it invisible to pytest's process
# tree (PPid=1) and nearly impossible to trace without explicit
# inotify/SHA watchdogs. Any test that legitimately needs to exercise
# the update-spawn path must mock subprocess.Popen explicitly.
cmd_str = _cmd_to_string(cmd)
low = cmd_str.lower()
if "update" in low and (
# hermes update / hermes update --gateway / setsid bash -c ... hermes update
("hermes" in low and "update" in low.split())
or
# python -m hermes_cli.main update --gateway
("hermes_cli" in low and "update" in low.split())
or
# venv/bin/hermes update (absolute path variant used in tests)
(".venv/bin/hermes" in low and "update" in low)
):
raise RuntimeError(
f"tests/conftest.py live-system guard: blocked "
f"subprocess.{name}({cmd!r}) — this command would run "
"`hermes update` against the real checkout, fetching "
"from origin and overwriting repo files (e.g. "
"pyproject.toml) mid-test-run. This corrupts every "
"subsequent subprocess in the same runner. "
"Mock subprocess.Popen (and subprocess.run if used) "
"in the test instead, or mark with "
"@pytest.mark.live_system_guard_bypass if genuinely "
"needed (e.g. an integration test testing the update "
"flow against a dedicated throwaway repo)."
)
def _wrap_subprocess(name, real):
def _guarded(cmd, *args, **kwargs):