Keep containers alive as root; notice containers that exit on their own #71
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2eabf6972b |
Address review of the keepalive / container-watch change
Secret Scan / scan (push) Successful in 9s
Build App (Preview) / compute-version (pull_request) Successful in 9s
Secret Scan / scan (pull_request) Successful in 8s
Build App (Preview) / create-release (pull_request) Successful in 2s
Build App (Preview) / test (pull_request) Successful in 4m10s
Build App (Preview) / build-macos (pull_request) Successful in 3m12s
Build App (Preview) / build-linux (pull_request) Successful in 9m31s
Build App (Preview) / build-windows (pull_request) Successful in 6m47s
Build App (Preview) / prune-previews (pull_request) Successful in 3s
Build Container / build-container (pull_request) Successful in 17m21s
- Event renamed to `project-container-exited` and carries a reason: `exited` (died while the app was watching) toasts; `found_stopped` (the first look of a run, e.g. after a host reboot) updates silently. Reconnect sweeps report `exited`, since the app was running then. - listen() in useProjectStatusEvents gets a .catch, like its sibling. - Keepalive respawns are throttled to ~1/s with a fork-free pause (`read -t` on a FIFO, `sleep 1` fallback), so a failing fork or an instantly-dying sleep cannot spin a core. Measured: 10748 respawns in 3 s unthrottled vs 3. - Watcher backoff resets only after a working connection (an event delivered, or up for 60 s); ping-OK-but-events-fail now backs off to 60 s instead of re-sweeping every 5 s. - Every Docker call in the watcher (ping, sweep, inspect, stream) races the app's cancellation, so a wedged daemon cannot hold shutdown. - TOCTOU between the inspect and the flip: ProjectsStore now counts writes; running_on() returns a sighting at a revision and mark_exited() refuses with Changed if anything was written since (a stop+start of the same container id included), and settle() re-confirms. - The "Running on this container" rule lives once, in the store, with the 12-char guard on both ids, matched under the lock without cloning. - Marketplace readiness fallback also requires a root-owned process. - TECHNICAL.md no longer documents the `su … sleep infinity` ending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
eba0d95780 |
Keep containers alive as root; notice containers that exit on their own
Secret Scan / scan (push) Successful in 7s
Build App (Preview) / compute-version (pull_request) Successful in 12s
Secret Scan / scan (pull_request) Successful in 6s
Build App (Preview) / test (pull_request) Successful in 4m45s
Build App (Preview) / create-release (pull_request) Successful in 3s
Build App (Preview) / build-macos (pull_request) Successful in 3m16s
Build App (Preview) / build-linux (pull_request) Successful in 5m36s
Build App (Preview) / build-windows (pull_request) Failing after 6m38s
Build App (Preview) / prune-previews (pull_request) Skipped
Build Container / build-container (pull_request) Successful in 23m46s
The entrypoint ended in `exec su -s /bin/bash claude -c "exec sleep infinity"`, so a project container's whole lifetime was a sleep owned by the unprivileged claude user. Claude Code runs as claude and routinely runs `pkill sleep`, `pkill -f`, `kill -9 -1` while testing, and any of those stopped the container: every terminal disconnected and the app kept showing Running. Entrypoint: stay resident as root and wait on a root-owned sleep run as `triple-c-keepalive infinity`, respawned if killed, with a TERM/INT/HUP trap so `docker stop` returns at once (550 ms, exit 0, vs 2.6 s / 143 before). Unprivileged kills now get EPERM; `sudo pkill sleep` is survived by the respawn. The drop to claude dated from the initial commit with no stated purpose; its only reader was the marketplace readiness probe, which now accepts either generation of entrypoint. App: container_watch.rs holds one Docker event subscription filtered daemon-side to container `die`. A die naming a Running project's container is confirmed with an inspect, then ProjectsStore::mark_exited flips it to Stopped as a compare-and-set that skips any project a command holds the project lock on. Each (re)subscription re-checks Running projects and asks for events since just before it. The frontend applies the new `project-status-changed` event and toasts why the sessions died. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |