fix(tests): join agent-build threads; restore HERMES_TEST_IMAGE env

1. test_tui_gateway_server: the two _start_agent_build tests waited only
   for the _make_agent 'built' event, then popped the session while
   _build's tail was still running. The tail's session.info/error emit
   then landed on whatever _real_stdout a LATER test had patched in —
   the write_json concurrency test intermittently saw 9 lines instead
   of its own 8 (2-in-5 repro locally). Join the build thread (exposed
   as session['_agent_build_thread']) before popping. 10/10 clean
   full-file runs post-fix, was 2/5 failing.

2. docker.yml: the profile-action conversion dropped the step env —
   including HERMES_TEST_IMAGE, so all 25 per-file subprocesses each
   docker-built the image inside dind concurrently. That is the root
   cause of the 15-minute docker jobs and the teardown timeout storms.
   Restore it plus the blank-API-key policy vars.
This commit is contained in:
ethernet 2026-07-30 18:39:13 -04:00
parent 1e2d5dddc9
commit 6d018b15c3
2 changed files with 16 additions and 6 deletions

View file

@ -83,7 +83,6 @@ jobs:
cache-from: ${{ matrix.cache-from }}
cache-to: ${{ (github.event_name != 'pull_request') && matrix.cache-to || '' }}
# Run the docker-integration test suite against the freshly-built
# image already loaded into the local daemon (`:test`).
#
@ -104,7 +103,7 @@ jobs:
# Pinned: unpinned setup-uv fetches a 'latest' manifest from
# raw.githubusercontent.com every job; transient fetch failures
# fail the job (2026-07-28 incident). Keep in sync with tests.yml.
version: "0.9.28"
version: '0.9.28'
- name: Set up Python 3.11 (for docker tests)
run: uv python install 3.11
@ -119,13 +118,10 @@ jobs:
command: uv sync --locked --python 3.11 --extra dev
- name: Run docker integration tests
# The test runner auto-caps workers for tests/docker (the suite is
# bounded by the single dind dockerd, not CPU — see
# run_tests_parallel.py's docker-suite auto-cap).
uses: ./.github/actions/profile
with:
label: docker-tests
command: scripts/run_tests.sh tests/docker/ --file-timeout 600
command: HERMES_TEST_IMAGE="${{ env.IMAGE_NAME }}:test" scripts/run_tests.sh tests/docker/ --file-timeout 600
# ---------------------------------------------------------------------------
# Rebuild and push each architecture only after the unprivileged build/test

View file

@ -663,6 +663,14 @@ def test_profile_scoped_agent_build_starts_mcp_discovery_in_profile_home(
try:
server._start_agent_build(sid, session)
assert built.wait(timeout=2)
# Join the whole build thread: after `built` fires, _build's tail
# still emits session.info (or an error frame if the tail raises
# once the session is popped). An unjoined thread leaks that emit
# onto whatever _real_stdout a LATER test has patched in.
_t = session.get("_agent_build_thread")
if _t is not None:
_t.join(timeout=5)
assert not _t.is_alive(), "agent build thread leaked"
finally:
server._sessions.pop(sid, None)
@ -718,6 +726,12 @@ def test_profile_scoped_agent_build_installs_secret_scope(monkeypatch, tmp_path)
try:
server._start_agent_build(sid, session)
assert built.wait(timeout=2)
# Same leak class as above: join _build's tail before popping the
# session, or its post-build emit lands in a later test's stdout.
_t = session.get("_agent_build_thread")
if _t is not None:
_t.join(timeout=5)
assert not _t.is_alive(), "agent build thread leaked"
finally:
server._sessions.pop(sid, None)