feat(worktree): docker isolation toggle and image build flow#1350
Draft
mvanhorn wants to merge 4 commits intostablyai:mainfrom
Draft
feat(worktree): docker isolation toggle and image build flow#1350mvanhorn wants to merge 4 commits intostablyai:mainfrom
mvanhorn wants to merge 4 commits intostablyai:mainfrom
Conversation
Adds Docker as a fourth provider triple alongside local and SSH. Pure plumbing: pty / filesystem / git providers + docker engine detection + image build + container lifecycle + bind-mount path resolution. No user-facing UI yet, no dispatch wiring. Mirrors the SSH provider triple's API. Cross-platform: macOS (Docker Desktop / Colima), Linux (Docker Engine, rootless), Windows (Docker Desktop WSL2 named pipe). Tests cover happy and error paths through a docker-engine-fake.ts recorder, so no real Docker daemon is required. Foundation PR for the docker isolation series. Follow-ups will add the user-facing toggle and dispatch wiring.
- Allocate TTY for Docker PTY sessions via tty flag on spawnExec, so isatty() returns true for shells, vim, and ncurses programs. - Return originalContent + modifiedContent for working-tree diffs instead of returning a unified patch as modifiedContent. - Return blob contents for branch diffs and honor options.oldPath for renames, matching the local provider's GitDiffResult shape. - Preserve the new path as path and the old path as oldPath when parsing R100 rename entries (was reversed). - Surface git porcelain v2 u records as unresolved conflict entries so docker worktrees in conflict show their conflicted files. - Resolve repo default branch via origin/HEAD before browseFile, instead of hardcoding 'main' for remote file URLs.
Adds an opt-in "Isolate" toggle per worktree row. When enabled, Orca builds (or reuses cached) a Docker image via the foundation in the docker provider series and persists the choice. Default off. Repo-level default toggle in Repository Settings: "Isolate worktrees from this repo by default." Implementation: - 3 new IPC handlers in src/main/ipc/docker.ts (engine-status, build-image, set-worktree-isolation) with build-progress events - IsolateToggleButton.tsx covers off / building / on / disabled-no-docker states with vitest coverage - RepositoryPane gains the per-repo defaultIsolation toggle - Worktree shape extends with isolation field; defaultIsolation flows through worktree-remote so new worktrees inherit the repo default - vitest.config.ts updated so .test.tsx files are discovered for the new component tests
Building a local Docker image against an SSH repo's remote path would fail or target the wrong machine. Add a guard in both the main process and the renderer: - buildImage IPC returns an explicit error when repo.connectionId is set - IsolateToggleButton disables itself for SSH repos with a clear tooltip Local-only Docker isolation is the v1 surface. SSH-repo container isolation is a separate problem (would require routing the build through the SSH provider) and is out of scope for this series.
5 tasks
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.
Summary
Adds an opt-in "Isolate" toggle per worktree row plus a per-repo "Default isolation" setting. When enabled, Orca builds (or reuses cached) a Docker image via the foundation in #1349 and persists
isolation: 'docker'on the worktree. Default off, worktree-native stays the zero-friction default.This is PR 2 of 3 in the docker isolation series. PR 1 (#1349) shipped the provider foundation. PR 3 (TBD) wires the dispatch so the persisted isolation actually routes execution through the Docker provider triple.
Why this lands as its own PR: keeping persistence + UI separate from dispatch routing keeps each review surface narrow. Maintainers can review the worktree shape change and the toggle UX here without also reviewing changes to provider-dispatch / orca-runtime.
Screenshots
UI changes are limited to:
Screenshots will be added before merge once the dispatch wiring (PR 3) lands so the toggle does something visible end-to-end. PR 2 in isolation produces a build spinner and persists the choice; the visible payoff is in PR 3.
Testing
pnpm lintpnpm typecheckpnpm test --run src/renderer/src/components/sidebar/IsolateToggleButton.test.tsx src/main/ipc/docker.test.ts src/renderer/src/store/slices/worktrees.test.ts src/renderer/src/components/settings/RepositoryPane.test.tsxpnpm buildsetWorktreeIsolationpersistence.AI Review Report
A code review pass was run before submission. Risks checked and findings:
isolation: 'host' | 'docker'defaults to'host'for existing worktrees that don't carry the field. Repo-leveldefaultIsolation?: RepoIsolationDefaultis optional so existing repo records stay forward-compatible.{error: string}rather than throwing, and usesmainWindow.webContents.sendfor progress events. The 30s engine-status cache is module-scoped with a deterministic guard.IsolateToggleButton,docker.ts). Nohelpers/utilsintroduced..ts, not.d.ts.Two findings surfaced. One was fixed in this PR; one is intentionally deferred to PR 3:
repo.connectionId) cannot run a local Docker build against a remote path. Added a guard insrc/main/ipc/docker.tsthat rejects the build with a clear error, and the toggle disables itself for SSH-mounted repos with the tooltip"Docker isolation isn't available for SSH-mounted repos.". SSH-repo container isolation is a separate problem (would route the build through the SSH provider) and is out of scope for this series.isolation: 'docker'but no terminal/agent spawn path readsworktree.isolationyet, so today the metadata is set without affecting execution. This is intentional series progression — PR 3 wiresprovider-dispatch.tsandorca-runtime.tsso the persisted choice actually routes pty / filesystem / git operations through the Docker provider triple from feat(providers): docker provider foundation (pty / filesystem / git) #1349. Splitting persistence from routing keeps each PR's review surface narrow.Security Audit
Input validation:
setWorktreeIsolationaccepts only'host'or'docker'; any other value rejects with an error.buildImagerejects SSH repos before any docker shell-out.Command execution: the only docker shell-out goes through the engine client introduced in #1349, which builds argv arrays for
child_process.spawn(no shell). User strings (repo path, Dockerfile path) flow as separate argv entries.Path handling: Dockerfile resolution uses
path.join. Bind-mount paths route through the helper from #1349 with cross-platform tests.Auth, secrets, IPC surface: three new IPC handlers (
docker:engine-status,docker:build-image,docker:set-worktree-isolation) plus adocker:build-progressevent. No new outbound network surface from Orca itself. No secrets or credentials touched. No auth flow changes.Persistence: settings written to the existing local store via the
cardPropspattern from PR #1293. No new disk paths.Follow-up: PR 3's dispatch wiring will need its own audit covering routing decisions, container lifecycle (spawn / hibernate / terminate), and the Images panel.
Notes
This PR builds on #1349. Because that PR isn't merged yet, this PR's diff against
mainincludes PR 1's commits as well; the net diff vsmainis cumulative. After #1349 merges, this branch will rebase to drop the duplicate commits.Series:
Open as draft to make the dependency obvious. Will mark ready for review once #1349 merges.
X: @mvanhorn
AI was used for assistance.