Files

4.9 KiB

Row 305 (#1812, Vikunja 591): shell suites remove the compose network they create

Owner Gizmoduck. Reviewer Filbert. Gate owner Sage. Brief: docs/plans/2026-10-23_gate-env-rows.md, "Brief: shell suites remove the compose network they create (Vikunja 591)", at c16e943b9. Base c16e943b9. Mos approved the change in mos-1442 point 2. Mos approved the two new checks in mos-1445 under mos-1272, adding the presence half.

The leak, as the code has it at c16e943b9

  • packages/processing-control/src/containers.mjs:16-19. containerArgs builds docker compose run --rm .... --rm removes the container. The project's <project>_default network stays.
  • compose.yaml has no top-level name:, so compose names the project after the checkout directory. Every gate worktree left one network. By 10-23 they had used up Docker's address pools.

Does COMPOSE_PROJECT_NAME reach compose? Yes, on every path the two suites take

  • Task path. scripts/run-task.sh:29 execs node scripts/mosaic-task.mjs. Nothing scrubs the env before that: scripts/common.sh has no env -i, no unset and no COMPOSE. mosaic-task.mjs:326 sets spawnEnv = { ...process.env }, :405 passes env: spawnEnv to runContainer, and containers.mjs:25 passes those options to spawnSync('docker', ...).
  • test-release. release.sh activate calls health_check, and health_check calls run-task.sh at :54, which is the task path above. env MOSAIC_MOCK_RESPONSE=... adds to the env and keeps the rest. The suite calls only status, activate and rollback, so release.sh:122 compose build is off its path.
  • test-task seat cases (:240, :315-351). agent.sh:209 execs packages/processing-control/src/container-cli.mjs, and container-cli.mjs:16 calls runContainer with env: process.env.
  • build.sh:13, release.sh:122 and verify.sh:40 also run compose. Neither suite calls them.

Change (scripts/test-release.sh, scripts/test-task.sh only)

  • After SANDBOX="$(mktemp -d)", export COMPOSE_PROJECT_NAME=mosaic-test-<release|task>-<id>. <id> is the sandbox name after its last dot, lowercased, alphanumeric only.

  • remove_compose_network runs docker network rm "${COMPOSE_PROJECT_NAME}_default":

    • If there's no docker binary, it returns 0.
    • If the error says not found or No such network, it returns 0.
    • On any other failure it prints one stderr line naming the network, then returns 0. That includes a daemon that is down. Sage kept that line in sage-3367 (a).
  • Each existing EXIT trap keeps everything it did and gains ; remove_compose_network at the end. The trap's own exit status doesn't change the script's.

  • One new check per suite, just before the summary. That is after the last Docker step whether or not the live cases ran. Both halves sit inside a docker info branch:

    • the network is present under the run's project name before the explicit remove_compose_network (this proves the export reached compose);
    • after the remove, it is absent.

    A failing docker network ls fails the check. Without the daemon, the check prints a skip line.

  • No docker compose down, and no other network is touched. No product file changes.

Commits: 541dee2b7 (draft), 3bce852d5 (presence half, mos-1445).

Gate (Sage's slot, in a gap)

  • Before the first run, run docker network rm on a name that was never created and record the daemon's exact wording (sage-3367 b). If the wording matches neither pattern, stop and report to Sage.
  • Each suite runs whole from a gate worktree, under timeout -k 10 900. That is far more than 3x the row 302 gate's walls: test-release 3 s, test-task 16 s (mos-1275).
    • TMPDIR comes from mktemp -d /mnt/storage/scratch/tmp/gXXX.
    • Before and after each run, record docker network ls filtered to mosaic-test-. The run's network must be absent after.
    • Grep ^skip and name every skip.
  • A run killed by the timeout's SIGKILL skips the EXIT trap and leaks its one network. The name is per-run under mosaic-test-, so the gate's after-listing finds it and removes it by its exact name (Filbert N1; no code change).
  • If docker info works but there is no network to find, that's a first-run red. It goes to Sage for Mos, unfixed (mos-1445).

Mutants (mos-1445)

  • (a) Drop the export. The new check must go red, because compose uses the directory's project and nothing exists under the run's name.
  • (b) Make the function's docker network rm a no-op. The new check must go red, because the network is still present after the remove.
  • (c) Drop remove_compose_network from the trap. This one is a known survivor: the explicit call covers the normal path, and the trap covers early exits.

Each mutant runs on an isolated copy of the file. Restores are checked by sha256. After each mutant, the network it leaves is removed by its exact name (mos-1438). Mutant (a) leaves the worktree directory's <dir>_default, and mutant (b) leaves the run's ${COMPOSE_PROJECT_NAME}_default.