Skip to content

feat(devcontainer): import external --devcontainer configs into the workspace#717

Merged
skevetter merged 3 commits into
mainfrom
feat/external-devcontainer-import
Jul 22, 2026
Merged

feat(devcontainer): import external --devcontainer configs into the workspace#717
skevetter merged 3 commits into
mainfrom
feat/external-devcontainer-import

Conversation

@skevetter

@skevetter skevetter commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

When --devcontainer <path> points at a devcontainer config outside the repo, devsy previously flattened it to a single synthesized file at the repo root. That broke self-contained configs: a relative dockerfile / features / dockerComposeFile resolves against the config's own directory, so those sibling assets were lost, and the write also left the checkout with a dirty git status.

This imports the external config into the workspace as a native named devcontainer profile (.devcontainer/<binary>/) instead:

  • A self-contained "<folder>/devcontainer.json" is copied whole, so its sibling assets (Dockerfile, features, compose) travel with it and config-relative paths resolve. A bare file is copied on its own.
  • The profile is discovered natively via the existing .devcontainer/<name>/devcontainer.json mechanism — no special Origin plumbing.
  • The imported dir is added to .git/info/exclude so it doesn't dirty git status (local, reversible, never touches the external source repo).
  • A marker file identifies the imported dir; a collision with a project-owned .devcontainer/<binary>/ falls back to a suffixed dir (<binary>_<rand>).
  • Teardown: runner.Delete removes the imported profile for local-folder sources (alongside cleanupDeliveryVolume), so both up-delete and (future) ci teardown clean up through one lifecycle path.

Behavior

--devcontainer external-path routing: an in-repo relative path stays a config path (DevContainerPath); an external absolute path is left in DevContainerSource so the runner imports it. none / image:<ref> are unchanged.

Tests

pkg/devcontainer: self-contained folder (Dockerfile resolves), bare file, marker written, collision→suffix, re-import reuses own dir, git-exclude added + non-git-repo skip, runner.Delete removes the imported profile / no-ops for non-local sources, and cleanup preserves project-owned profiles. Plus an external-path pass-through case in the up source-resolution test.

Notes

Summary by CodeRabbit

  • New Features

    • Added support for using external devcontainer folders or configuration files.
    • External devcontainers are imported into the workspace with required files and tracked safely.
    • Imported configurations are excluded from Git when applicable.
    • Reusing an imported configuration avoids duplicate copies.
  • Bug Fixes

    • External absolute paths are now preserved correctly.
  • Cleanup

    • Imported devcontainer files are removed when the workspace is deleted.

…orkspace

Passing --devcontainer with a path to a config outside the repo previously
flattened it to a single synthesized file at the repo root, which broke
self-contained configs: a relative Dockerfile/feature/compose path resolves
against the config's directory, so the sibling assets were lost.

Import the external config into the workspace as a native named profile
(.devcontainer/<binary>/) instead:
- A self-contained "<folder>/devcontainer.json" is copied whole so its sibling
  assets travel with it; a bare file is copied on its own.
- The profile is discovered natively (.devcontainer/<name>/devcontainer.json),
  so config-relative paths resolve.
- The imported dir is added to .git/info/exclude so it does not dirty git
  status, and carries a marker file so teardown can identify it. A collision
  with a project-owned .devcontainer/<binary>/ falls back to a suffixed dir.
- runner.Delete removes the imported profile for local-folder sources
  (alongside cleanupDeliveryVolume), so both up-delete and ci teardown clean up
  through one path.
@netlify

netlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Deploy Preview for images-devsy-sh canceled.

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

@netlify

netlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

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

@coderabbitai

coderabbitai Bot commented Jul 22, 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: 35 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

Run ID: 4d76d827-0a63-4ea0-9101-43f2fe02e7ee

📥 Commits

Reviewing files that changed from the base of the PR and between 3d21663 and c04f654.

📒 Files selected for processing (2)
  • pkg/devcontainer/config.go
  • pkg/devcontainer/import_external_test.go
📝 Walkthrough

Walkthrough

Absolute external devcontainer paths now pass through workspace resolution. External configurations are copied into marked workspace-local profiles, optionally excluded from Git, parsed, and cleaned up during local workspace deletion.

Changes

External Devcontainer Lifecycle

Layer / File(s) Summary
Source path resolution
cmd/workspace/up/up_validate.go, cmd/workspace/up/up_devcontainer_source_test.go
Absolute external paths remain in DevContainerSource; only absolute paths are copied into DevContainerPath, with corresponding pass-through coverage.
External profile import
pkg/devcontainer/config.go, pkg/devcontainer/import_external_test.go
SourcePath imports files or self-contained directories into marked .devcontainer/*-imported profiles, handles collisions and reuse, updates Git excludes, and parses the copied configuration.
Imported profile cleanup
pkg/devcontainer/delete.go, pkg/devcontainer/delete_test.go, pkg/devcontainer/config.go
Local workspace deletion removes imported profiles while preserving project-owned profiles; non-local sources skip cleanup.

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

Sequence Diagram(s)

sequenceDiagram
  participant SourcePath
  participant DevcontainerRunner
  participant WorkspaceFilesystem
  participant ConfigParser
  SourcePath->>DevcontainerRunner: provide external devcontainer path
  DevcontainerRunner->>WorkspaceFilesystem: copy files into imported profile
  DevcontainerRunner->>WorkspaceFilesystem: write marker and Git exclude entry
  DevcontainerRunner->>ConfigParser: parse copied devcontainer.json
  ConfigParser-->>DevcontainerRunner: return imported configuration
  DevcontainerRunner->>WorkspaceFilesystem: remove marked profiles during Delete
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: importing external devcontainer configs into the workspace.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

@skevetter
skevetter marked this pull request as ready for review July 22, 2026 22:14

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/devcontainer/config.go (1)

247-252: 🔒 Security & Privacy | 🔵 Trivial | 🏗️ Heavy lift

"Self-contained" detection copies the entire sibling directory unfiltered.

Any single sibling entry marks the config as self-contained and triggers a full recursive copy of filepath.Dir(srcPath) via copypkg.Directory. If an external --devcontainer path lives outside a dedicated .devcontainer/ folder (e.g. at a project root), this can pull in large trees, VCS metadata, or credentials into the workspace — and excludeFromGit is best-effort only, so nothing guarantees these stay untracked. Consider narrowing the heuristic (e.g. only copy .devcontainer-adjacent files/known devcontainer asset patterns) or documenting the risk.

Also applies to: 268-281

🤖 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 `@pkg/devcontainer/config.go` around lines 247 - 252, Restrict
isSelfContainedDevContainer and the surrounding copy flow so a self-contained
configuration is recognized only within a dedicated devcontainer directory or
approved devcontainer asset set. Do not recursively copy filepath.Dir(srcPath)
via copypkg.Directory for external paths such as a project-root configuration;
copy only the required configuration and related assets instead.
🤖 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.

Inline comments:
In `@pkg/devcontainer/config.go`:
- Around line 206-209: Preserve the original srcPath value before the
filepath.Abs call in the surrounding devcontainer path-resolution logic, then
use that preserved value in the error returned by the Abs failure branch. Keep
the successful resolved path assigned to srcPath and continue wrapping the
original error.

---

Nitpick comments:
In `@pkg/devcontainer/config.go`:
- Around line 247-252: Restrict isSelfContainedDevContainer and the surrounding
copy flow so a self-contained configuration is recognized only within a
dedicated devcontainer directory or approved devcontainer asset set. Do not
recursively copy filepath.Dir(srcPath) via copypkg.Directory for external paths
such as a project-root configuration; copy only the required configuration and
related assets instead.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd1e0828-8b72-4019-8937-af395fdb5cd6

📥 Commits

Reviewing files that changed from the base of the PR and between bd6c326 and 3d21663.

📒 Files selected for processing (6)
  • cmd/workspace/up/up_devcontainer_source_test.go
  • cmd/workspace/up/up_validate.go
  • pkg/devcontainer/config.go
  • pkg/devcontainer/delete.go
  • pkg/devcontainer/delete_test.go
  • pkg/devcontainer/import_external_test.go

Comment thread pkg/devcontainer/config.go Outdated
Address review feedback:
- Only treat an external config as self-contained (folder copy) when it lives
  in a dedicated .devcontainer directory or a .devcontainer/<name> profile,
  matching the spec's layout. A config at a project root now copies just the
  file, so it can't pull in arbitrary sibling trees (secrets, .git, ...).
- Preserve the original srcPath in the filepath.Abs error message instead of
  the empty string Abs returns on failure.
@skevetter
skevetter merged commit aea85a4 into main Jul 22, 2026
64 checks passed
@skevetter
skevetter deleted the feat/external-devcontainer-import branch July 22, 2026 23:13
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