Skip to content

feat(up): reopen dev container in recovery mode on build failure - #785

Merged
skevetter merged 6 commits into
mainfrom
feat/recovery-container
Jul 27, 2026
Merged

feat(up): reopen dev container in recovery mode on build failure#785
skevetter merged 6 commits into
mainfrom
feat/recovery-container

Conversation

@skevetter

@skevetter skevetter commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a "Reopen in Recovery Container" capability so a failed dev container build no longer leaves users locked out of their workspace.

CLI

  • New up --recovery flag: on a build failure, rebuilds a stripped-down container — features and lifecycle hooks removed. A plain image is reused as-is; a failing Dockerfile is swapped for the generic base image (language.None). Compose configs are unsupported and decline cleanly with a warning.
  • Recovery-eligible build failures are classified as a structured BUILD_FAILED_RECOVERABLE error and exit with code 79 (just past sysexits EX__MAX).
  • Recovery containers are labeled sh.devsy.recovery=true; the label is read back on container reuse so recovery state survives restarts.
  • workspace status --recovery surfaces the running container's recovery state (opt-in, so the frequent status poll pays no extra I/O).

Desktop

  • Build-failure banner with Reopen in Recovery Container / Retry / Show Log, driven by the structured error code (not brittle string matching).
  • Recovery-mode indicator; inRecovery is set from the authoritative up result and reconciled against the live container on mount (covers rebuilds done outside the app).
  • Persisted per-workspace state with stale-banner reconciliation.
  • On build failure preference (prompt / auto-recovery / do nothing).

Tests

  • Go unit: config stripping/non-mutation, Dockerfile→image swap, recovery label detection, error classification through multi-%w wrapping, envelope/result round-trips.
  • e2e (up-handle-errors): failing build exits 79, --recovery succeeds, container reachable over SSH.
  • Desktop vitest: classifier, envelope/status parsing, arg threading, settings.

Live Docker execution of the recovery path is verified via the e2e harness (task cli:test:e2e:focus -- up-handle-errors).

Summary by CodeRabbit

  • New Features

    • Added recovery-container support for dev container build failures.
    • Added --recovery options for workspace start and status commands.
    • Added automatic, prompted, or disabled recovery behavior in Settings.
    • Added recovery status indicators, banners, and actions in workspace views.
    • Added a distinct exit code for recoverable build failures.
  • Bug Fixes

    • Improved error reporting and recovery guidance when builds fail.
    • Recovery containers now skip features and lifecycle commands to enable repairs.

When a dev container build fails, allow launching a stripped-down recovery
container (features and lifecycle commands disabled; a failed Dockerfile
swapped for a known-good base image) so the workspace stays reachable and the
user can repair devcontainer.json and rebuild.

- CLI: add `--recovery` to `up`; classify recoverable build failures with a
  structured code (exit 79) and label recovery containers `sh.devsy.recovery`.
- Recovery is declined for compose configs and skips a failing initializeCommand.
- Persist/expose recovery state via the up result and `workspace status
  --recovery` so the desktop can reconcile it (in-app and external rebuilds).
- Desktop: build-failure banner (Reopen in Recovery / Retry / Show Log),
  recovery-mode indicator, and an on-build-failure preference.
@netlify

netlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit ae8c5df
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a67c53d86ee6400089fcd4e

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@skevetter, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e57d9c51-1802-4286-9998-6a22088512d3

📥 Commits

Reviewing files that changed from the base of the PR and between fdc669e and ae8c5df.

📒 Files selected for processing (1)
  • desktop/src/main/ipc.ts
📝 Walkthrough

Walkthrough

This change adds recoverable devcontainer build failures with a recovery-container retry path. CLI and IPC layers forward recovery options and metadata, while the desktop adds build-failure preferences, persisted recovery state, recovery actions, and status indicators.

Changes

Recovery error and execution path

Layer / File(s) Summary
Error, result, and serialization contracts
pkg/clierr/errors.go, pkg/devcontainer/config/*, pkg/exitcode/*, pkg/config/labels.go, pkg/client/client.go
Recoverable build errors, result metadata, recovery labels, status fields, success-envelope fields, and exit code 79 are added.
Devcontainer recovery execution
pkg/devcontainer/run.go, pkg/devcontainer/single.go, pkg/devcontainer/single_test.go
Failed builds retry with features and lifecycle actions removed; recovery containers are labeled, detected, and reported, while initialization is skipped during recovery.
CLI recovery wiring
cmd/root.go, cmd/internal/agentworkspace/up.go, cmd/workspace/status.go, cmd/workspace/up/*, pkg/flags/names/names.go, pkg/provider/workspace.go
Workspace commands accept --recovery, propagate recovery metadata, classify recoverable failures, and expose recovery status in JSON output.

Desktop recovery workflow

Layer / File(s) Summary
IPC and command forwarding
desktop/src/main/ipc.ts, desktop/src/renderer/src/lib/ipc/*
Workspace status and up commands forward recovery options, and streaming failures include structured CLI error metadata.
Preferences and recovery state
desktop/src/renderer/src/lib/stores/settings.ts, desktop/src/renderer/src/pages/SettingsPage.svelte, desktop/src/renderer/src/lib/utils/log-parser.ts
Build-failure behavior can be set to prompt, auto-recovery, or nothing; per-workspace state is persisted and recovery envelopes are parsed.
Launch and workspace controls
desktop/src/renderer/src/lib/components/workspace/*, desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
Launch and workspace detail flows offer recovery retries, auto-recovery, recovery badges, failure banners, and full-container rebuild actions.
Integration coverage
e2e/tests/up/*
A failing Dockerfile fixture verifies the recoverable exit code and successful SSH access after recovery.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Desktop
  participant CLI
  participant Devcontainer
  participant RecoveryContainer
  Desktop->>CLI: workspace up
  CLI->>Devcontainer: build configured container
  Devcontainer-->>CLI: recoverable build failure
  CLI-->>Desktop: structured error and recovery metadata
  Desktop->>CLI: workspace up --recovery
  CLI->>RecoveryContainer: build stripped recovery configuration
  RecoveryContainer-->>Desktop: recovery success envelope
Loading

Possibly related PRs

  • devsy-org/devsy#698: Extends the same CLI error-classification and exit-code boundary used for recoverable build failures.
  • devsy-org/devsy#624: Modifies the same runner.Up initialization and dispatch control flow.
  • devsy-org/devsy#780: Modifies the same workspace wizard launch and retry gating path.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: reopening the dev container in recovery mode after a build failure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@netlify

netlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploy Preview for images-devsy-sh canceled.

Name Link
🔨 Latest commit ae8c5df
🔍 Latest deploy log https://app.netlify.com/projects/images-devsy-sh/deploys/6a67c53d806ea200087d2986

@skevetter
skevetter marked this pull request as ready for review July 27, 2026 20:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
desktop/src/main/ipc.ts (1)

866-902: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

workspace_rebuild/workspace_reset never forward cliError, silently breaking recovery detection for those ops.

WorkspaceDetailPage.svelte's BUILD_OPS set includes "Rebuild" and "Reset" and calls isRecoverableBuildFailure(progress.cliError) for every op in that set. But these two handlers' runStreaming exit callbacks are still (code) => { void sink.done(formatLogLine(...)) } — unlike the fixed workspace_up callback (lines 765-774), they never pass cliError through to sink.done. As a result, progress.cliError will always be undefined for Rebuild/Reset failures, so the recovery banner/auto-recovery flow can never trigger for those two operations, even though the desktop UI is built to support it.

🔧 Apply the same fix used for workspace_up
       cli.runStreaming(
         cliArgs,
         (line) => {
           if (!sink.line(formatLogLine(line))) return logStore.onDrain(logPath)
         },
-        (code) => {
-          void sink.done(
-            formatLogLine(`Exit code: ${code}`, code === 0 ? "INFO" : "ERROR"),
-          )
-        },
+        (code, cliError) => {
+          void sink.done(
+            formatLogLine(`Exit code: ${code}`, code === 0 ? "INFO" : "ERROR"),
+            code === 0 ? undefined : { level: "error", cliError },
+          )
+        },
         args.workspaceId,
       )

Apply to both the workspace_rebuild and workspace_reset handlers.

Also applies to: 904-940

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/src/main/ipc.ts` around lines 866 - 902, Update the exit callbacks in
both the workspace_rebuild and workspace_reset IPC handlers to receive the
runStreaming cliError argument and forward it to sink.done, matching the
existing workspace_up behavior. Preserve the current exit-code log formatting
while ensuring failed operations populate progress.cliError for recovery
detection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@desktop/src/main/ipc.ts`:
- Around line 866-902: Update the exit callbacks in both the workspace_rebuild
and workspace_reset IPC handlers to receive the runStreaming cliError argument
and forward it to sink.done, matching the existing workspace_up behavior.
Preserve the current exit-code log formatting while ensuring failed operations
populate progress.cliError for recovery detection.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2e3123b4-4b2a-445e-9503-c88b5a0bc7d3

📥 Commits

Reviewing files that changed from the base of the PR and between 6fbcdfd and fdc669e.

📒 Files selected for processing (33)
  • cmd/internal/agentworkspace/up.go
  • cmd/root.go
  • cmd/workspace/status.go
  • cmd/workspace/up/up.go
  • cmd/workspace/up/up_flags.go
  • desktop/src/main/ipc.ts
  • desktop/src/renderer/src/lib/components/workspace/WorkspaceCard.svelte
  • desktop/src/renderer/src/lib/components/workspace/WorkspaceWizard.svelte
  • desktop/src/renderer/src/lib/ipc/commands.test.ts
  • desktop/src/renderer/src/lib/ipc/commands.ts
  • desktop/src/renderer/src/lib/stores/settings.test.ts
  • desktop/src/renderer/src/lib/stores/settings.ts
  • desktop/src/renderer/src/lib/utils/log-parser.test.ts
  • desktop/src/renderer/src/lib/utils/log-parser.ts
  • desktop/src/renderer/src/pages/SettingsPage.svelte
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
  • e2e/tests/up/handles_errors.go
  • e2e/tests/up/testdata/docker-recovery/.devcontainer.json
  • e2e/tests/up/testdata/docker-recovery/Dockerfile
  • pkg/client/client.go
  • pkg/clierr/errors.go
  • pkg/clierr/errors_test.go
  • pkg/config/labels.go
  • pkg/devcontainer/config/envelope.go
  • pkg/devcontainer/config/envelope_test.go
  • pkg/devcontainer/config/result.go
  • pkg/devcontainer/config/result_test.go
  • pkg/devcontainer/run.go
  • pkg/devcontainer/single.go
  • pkg/devcontainer/single_test.go
  • pkg/exitcode/codes.go
  • pkg/flags/names/names.go
  • pkg/provider/workspace.go

@skevetter
skevetter marked this pull request as draft July 27, 2026 20:52
@skevetter
skevetter marked this pull request as ready for review July 27, 2026 21:20
@skevetter
skevetter merged commit c3adde3 into main Jul 27, 2026
115 of 117 checks passed
@skevetter
skevetter deleted the feat/recovery-container branch July 27, 2026 21:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant