fix(up): foreground (non-detached) up no longer crashes on startup#135
Merged
Conversation
`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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #131.
Problem
container-compose upwithout-dstarts the first container, then exits 1:Nothing is left running.
up -dworks fine. Reproduces with a minimal single-service compose (no network/healthcheck/depends_on).Root cause
In foreground mode the attached
container runsubprocess runs in a backgroundTaskwhilewaitUntilServiceStartedpolls concurrently. Apple Container reports a container as.stoppedthroughout image/kernel fetch and only flips to.runningonce init starts (verified by rapid-polling an attached container —.stoppedfor the whole fetch phase). An early poll caught that pre-running.stopped, treated it as a completed one-shot, and threw viacontainerWait(which needs the runtime client — gone once the container is stopped).Detached mode is immune:
container run -donly returns after the container is running, so.stoppedis never observed during startup.Fix
ForegroundRunHandleactor tracks the attachedcontainer runsubprocess. InwaitUntilServiceStarted,.stoppedis treated as terminal only once that subprocess has exited; while it is alive, keep polling for.running. Detached mode (handle isnil) keeps its existing behaviour.waitUntilAllContainersStopped'sseenRunningwith all container names. It runs only afterconfigServicehas confirmed every container started, so a.stoppedcontainer there has exited rather than not-yet-started. Without this, a fast one-shot that exits before the first poll would never be seen.runningand foregroundupwould hang forever.Bonus
This also makes #127's signal handling actually reachable — previously
configServicecrashed beforerunForegroundUntilStoppedcould install the SIGINT/SIGTERM handler, so Ctrl-C never gracefully stopped the project.Verification
sleep infinity):upbrings the container up and stays attached; SIGINT → "Gracefully stopping..." → container stopped, process exits 0.echo hello): runs, prints output, then "All containers have stopped." and exits 0 on its own (previously hung).