Skip to content

fix(up): foreground (non-detached) up no longer crashes on startup#135

Merged
Cyb3rDudu merged 1 commit into
mainfrom
fix/foreground-up
Jul 12, 2026
Merged

fix(up): foreground (non-detached) up no longer crashes on startup#135
Cyb3rDudu merged 1 commit into
mainfrom
fix/foreground-up

Conversation

@Cyb3rDudu

Copy link
Copy Markdown
Collaborator

Fixes #131.

Problem

container-compose up without -d starts the first container, then exits 1:

Error: no runtime client exists: container is stopped

Nothing is left running. up -d works fine. Reproduces with a minimal single-service compose (no network/healthcheck/depends_on).

Root cause

In foreground mode the attached container run subprocess runs in a background Task while waitUntilServiceStarted polls concurrently. Apple Container reports a container as .stopped throughout image/kernel fetch and only flips to .running once init starts (verified by rapid-polling an attached container — .stopped for the whole fetch phase). An early poll caught that pre-running .stopped, treated it as a completed one-shot, and threw via containerWait (which needs the runtime client — gone once the container is stopped).

Detached mode is immune: container run -d only returns after the container is running, so .stopped is never observed during startup.

Fix

  • New ForegroundRunHandle actor tracks the attached container run subprocess. In waitUntilServiceStarted, .stopped is treated as terminal only once that subprocess has exited; while it is alive, keep polling for .running. Detached mode (handle is nil) keeps its existing behaviour.
  • Seed waitUntilAllContainersStopped's seenRunning with all container names. It runs only after configService has confirmed every container started, so a .stopped container there has exited rather than not-yet-started. Without this, a fast one-shot that exits before the first poll would never be seen .running and foreground up would hang forever.

Bonus

This also makes #127's signal handling actually reachable — previously configService crashed before runForegroundUntilStopped could install the SIGINT/SIGTERM handler, so Ctrl-C never gracefully stopped the project.

Verification

  • Long-running service (sleep infinity): up brings the container up and stays attached; SIGINT → "Gracefully stopping..." → container stopped, process exits 0.
  • One-shot service (echo hello): runs, prints output, then "All containers have stopped." and exits 0 on its own (previously hung).
  • 221/222 tests pass; the one failure is the known transient WordPress "failed to bootstrap container" flake (detached, unrelated — passes on retry).

`container-compose up` without `-d` started the first container, then exited 1
with "Error: no runtime client exists: container is stopped", leaving nothing
running. Detached `up -d` was unaffected.

Root cause: in foreground mode the attached `container run` subprocess runs in
a background Task while `waitUntilServiceStarted` polls concurrently. Apple
Container reports a container as `.stopped` throughout the image/kernel fetch
and only flips to `.running` once init starts, so an early poll caught that
pre-running `.stopped`, treated it as a completed one-shot, and threw via
`containerWait` (which needs the runtime client, gone once stopped).

Fixes:
- Track the attached `container run` subprocess with a `ForegroundRunHandle`.
  In `waitUntilServiceStarted`, only treat `.stopped` as terminal once that
  subprocess has actually exited; while it is alive, keep polling for
  `.running`. Detached mode (handle is nil) keeps its existing behaviour.
- Seed `waitUntilAllContainersStopped`'s `seenRunning` with all container
  names. It runs only after `configService` has already confirmed every
  container started, so a `.stopped` container there is one that exited, not
  one that hasn't started. Without this, a fast one-shot that exits before the
  first poll would never be seen `.running` and foreground `up` would hang.

This also makes #127's signal handling reachable: previously `configService`
crashed before `runForegroundUntilStopped` could install the SIGINT/SIGTERM
handler, so Ctrl-C never gracefully stopped the project.

Fixes #131
@Cyb3rDudu Cyb3rDudu merged commit 23449fa into main Jul 12, 2026
1 check passed
@Cyb3rDudu Cyb3rDudu deleted the fix/foreground-up branch July 12, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

up without -d fails: "no runtime client exists: container is stopped"

1 participant