Commit graph

40 commits

Author SHA1 Message Date
KyleBarton
cd217ea49d
Trim nonalphanumeric chars before prefixing image tag name (#54578)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #54452

Release Notes:

- Fixed bug where the sixth character of a devcontainer name is
nonalpanumeric
2026-04-23 07:36:36 +00:00
Toni Alatalo
f939ec6049
dev_container: Align compose project name with reference CLI (#54302)
> **Draft / open question for maintainers.** The failure mode this fixes
is narrow — a new-Zed-created container exists under the `name`-field
project while a CLI-derivation tool (`@devcontainers/cli`, VS Code)
operates on the same folder (the container persists in Docker, so the
originating Zed session doesn't need to still be open). See issue #54255
failure mode 3 and the fixture's step 6.
>
> I'd like to pose this as a question rather than a claim: is matching
`@devcontainers/cli`'s `getProjectName` precedence something the project
wants to take on, given the narrowness of the bug? I wrote this
implementation mostly as a way to explore what parity would actually
cost — happy to close it if you'd rather leave it as-is, or pare it down
(e.g. just rule 4) if a partial match is preferable.
>
> The broader value beyond this specific bug: devcontainer impls
agreeing on the same project name means containers created by Zed, the
devcontainer CLI, and VS Code are interchangeable for the same folder,
which feels worth it to me — but you know the project's priorities
better.
>
> Folds in #54068 (detection) — closing that PR unmerged; its
`MultipleMatchingContainers` error lands here.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #54255

## Summary

**Match `@devcontainers/cli`'s full `getProjectName` precedence.**
Replaces `safe_id_lower(devcontainer.json's name)` with the five-step
chain the reference CLI walks (see [`src/spec-node/dockerCompose.ts` in
devcontainers/cli](https://github.com/devcontainers/cli/blob/main/src/spec-node/dockerCompose.ts)):

1. `COMPOSE_PROJECT_NAME` from the local environment.
2. `COMPOSE_PROJECT_NAME=` in the workspace `.env` file.
3. Top-level `name:` on the merged compose config, when at least one
fragment declared it explicitly.
4. `${workspaceFolderBasename}_devcontainer` — only when the first
compose file's directory is `<workspace>/.devcontainer/`.
5. Otherwise, the plain basename of the first compose file's directory
(no suffix).

The old Zed implementation diverged at every one of those inputs: any
user setting `COMPOSE_PROJECT_NAME`, shipping a `.env` with one,
declaring a top-level compose `name:`, or pointing `dockerComposeFile`
outside `.devcontainer/` (e.g. `"../docker-compose.yml"`) got a
different project namespace than the CLI and VS Code, producing two
compose projects for the same folder.

Adds a small `sanitize_compose_project_name()` helper implementing the
CLI's rules (lowercase + strip `[^-_a-z0-9]`) — notably preserving
hyphens, which `safe_id_lower` would have replaced with underscores.

Adds two helpers used by the precedence walk:
- `parse_dotenv_compose_project_name` — line scan extracting
`COMPOSE_PROJECT_NAME=…` from the workspace `.env`, matching the subset
the CLI's regex dotenv reader recognizes.
- `compose_fragment_declares_name` — parses each compose fragment with
`yaml-rust2` (already a transitive workspace dep; slated to become a
direct dep via #53922) and checks for a `name` key on the root mapping
(block, quoted, or flow style all work), matching the CLI's own
`yaml.load`. `docker compose config` always injects `name: devcontainer`
into its merged output when no fragment declared one, so rule 3 needs to
distinguish the user-provided case from the injected default — this
helper supplies that signal. On YAML parse failure it returns "not
declared" (rule 4 applies), matching the CLI's fallback.

`project_name()` becomes async and fallible (`async fn
project_name(&self) -> Result<String, DevContainerError>`) so it can
load the `.env` file and each compose fragment via `self.fs.load`. Four
call sites now `.await?` the derivation. Real I/O errors on the `.env`
read propagate as `FilesystemError` (matching the CLI's narrow
`ENOENT`/`EISDIR` swallow); fragment-rescan read errors are logged and
skipped (matching the CLI's broader try/catch over its fragment read +
parse).

The `name` field is still used as the features image-tag prefix
(`generate_features_image_tag`); only the compose project namespace is
decoupled from it.

**Duplicate-container detection (from #54068).** When
`check_for_existing_container`'s label-based lookup returns more than
one match, propagate `MultipleMatchingContainers(ids)` with instructions
to clean up the stale one(s). This covers the mixed-version upgrade edge
case where a pre-fix Zed left a container under the legacy project name
alongside a CLI-style one — transparent to users in the common case (one
tool, one container), explicit error when two legacy siblings need
manual cleanup.

## Why

Full write-up with verified fixtures and captured output: #54255.

Three failure modes from the same root cause, all resolved by this
change:

1. **Interop** — opening a folder in both Zed and `devcontainer up` (or
Zed and VS Code) creates two compose projects with identical
`devcontainer.local_folder` + `devcontainer.config_file` labels,
breaking the spec's uniqueness invariant.
2. **Cross-worktree silent db/volume reuse** — if multiple git worktrees
share a `devcontainer.json` with the same `name`, Zed uses the same
compose project for all of them; Compose reuses stateful siblings (db,
cache, localstack) by config-hash, so worktree B silently inherits
worktree A's database. Fixture + captured output:
[antont/zed-devcontainer-db-share-repro](https://github.com/antont/zed-devcontainer-db-share-repro).
3. **Mixed-version Zed sessions** — the Rust impl landed in stable
v0.232.2 (2026-04-15, #52338). Older Zed (≤v0.231.x) shelled out to
`@devcontainers/cli` so it used the reference derivation. The collision
shows up when a new-Zed-created container exists under the name-field
project while a CLI-derivation tool (old Zed, `devcontainer up`, VS
Code) operates on the same folder.

## Migration / compatibility

Existing Zed-created containers (under the old `safe_id_lower(name)`
project) continue to be found via `check_for_existing_container`'s
label-based lookup — they're looked up by `devcontainer.local_folder` +
`devcontainer.config_file`, not by project name. A user with duplicate
legacy containers from a prior Zed session sees
`MultipleMatchingContainers` with cleanup instructions.

## Revision — 2026-04-22

Revised per @KyleBarton review on the prior version:
- Swapped the YAML parser from `serde_yaml_ng` to `yaml-rust2` (already
transitive via `tree-sitter-yaml`; net reduction of one direct workspace
dep; also what #53922 will pull in).
- Dropped the mixed-version tiebreak (`pick_canonical_container`) and
its `com.docker.compose.project` serde label. The edge case it covered
is transient enough to address via the explicit
`MultipleMatchingContainers` error rather than permanent tiebreaking
code.
- Folded #54068's detection commit into this PR; #54068 closed unmerged.
- Rebased onto `main`.

## Test plan

- [x] `cargo test -p dev_container --lib` — 89 passed, including:
  - `sanitize_compose_project_name_matches_cli_rules`
- `--project-name` assertion added to
`test_spawns_devcontainer_with_docker_compose`
  - `check_for_existing_container_errors_when_multiple_match`
  - `derive_project_name_env_wins_over_everything`
  - `derive_project_name_dotenv_wins_over_compose_and_fallback`
  - `derive_project_name_compose_name_wins_over_fallback`
- `derive_project_name_skips_compose_name_when_not_explicitly_declared`
-
`derive_project_name_omits_suffix_when_compose_file_outside_devcontainer_dir`
  - `derive_project_name_normalizes_compose_path_for_rule_4`
- `compose_fragment_declares_name_detects_top_level_name_key` (covers
block, quoted-key, and flow-style roots, plus parse failure →
not-declared)
  - `is_missing_file_error_only_accepts_notfound_and_isadirectory`
- [x] `cargo fmt --all` — clean
- [x] `./script/clippy -p dev_container` — clean
- [x] **End-to-end with fixture**
[antont/zed-devcontainer-compose-test](https://github.com/antont/zed-devcontainer-compose-test):
  - Build `zed` from this branch.
- Clean slate: `docker ps -a --filter
"label=devcontainer.local_folder=$PWD" -q | xargs -r docker rm -f`
- `zed --dev-container /path/to/devcontainer-compose-test` → Zed creates
container under project `devcontainer-compose-test_devcontainer` (was
`compose_duplicate_repro` before the fix).
- `devcontainer up --workspace-folder $PWD` → CLI reports the same
`containerId` Zed created; no second compose project is introduced.
- Captured: `devcontainer-compose-test_devcontainer-app-1`,
`composeProjectName: "devcontainer-compose-test_devcontainer"` reported
by both tools.

Release Notes:

- Fixed dev container Docker Compose project name now matches the full
`getProjectName` precedence from the reference devcontainer CLI
(`COMPOSE_PROJECT_NAME` in the environment, then in the workspace
`.env`, then an explicit top-level `name:` on the merged compose config,
then the basename of the first compose file's directory — with the
`_devcontainer` suffix only when that directory is
`<workspace>/.devcontainer`). This prevents duplicate containers when
the same folder is opened with both Zed and the devcontainer CLI / VS
Code.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 16:56:14 -07:00
Sandro Meier
bcaa1b5231
dev_container: Resolve compose service build args in Dockerfile expansion (#54270)
For docker-compose-based dev containers, build args live on the primary
compose service's `build.args`, not on `dev_container.build` (which is
`None` in the compose case). `expanded_dockerfile_content` only
consulted the latter, so `${…}` references in the service's Dockerfile —
e.g. `FROM ${BASE_IMAGE}` — were never substituted. Downstream callers
then invoked `docker inspect "${BASE_IMAGE}"` and `docker pull
"${BASE_IMAGE}"`, both of which fail and the dev container fails to
start.

A new unit test `test_expands_compose_service_args_in_dockerfile` mounts
a Dockerfile with `FROM ${BASE_IMAGE}` backed by a compose service whose
`build.args` define `BASE_IMAGE=test_image:latest`, and asserts both
`expanded_dockerfile_content` and `image_from_dockerfile` produce the
resolved reference.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed dev container startup failing for docker-compose configs whose
service Dockerfile uses build-arg substitution in the FROM line (for
example, `FROM ${BASE_IMAGE}`).
2026-04-21 16:44:44 -07:00
Ivan Mironov
66a77d29fd
Fix devcontainer localEnv/containerEnv substitution when variable is undefined (#53728)
Existing code replaces only the variables that are present in the
environment. It ignores references to undefined variables and leaves
them as is.

Consider following devcontainer.json:

```json
{
  ...
  "remoteEnv": {
    "RUSTFLAGS": "${localEnv:RUSTFLAGS}"
  },
  ...
}
```

Existing code will set RUSTFLAGS inside the dev container to a correct
value only if RUSTFLAGS is defined outside the dev container. If
RUSTFLAGS is not defined outside, Zed will erroneously set RUSTFLAGS to
`${localEnv:RUSTFLAGS}`.

This patch fixes this by replacing such references with either empty
strings or provided default values (`${localEnv:VARNAME:default}`).

This patch also removes replacement of `\` with `/` for environment
variables. Existing code makes no sense as simple replacement will not
magically make Windows paths valid on *nix. It is also guaranteed to
break things: think about passing passwords via env vars, for example.

I also noticed that, for some reason, existing code serializes k-v maps
to JSON, then tries to replace the references, and after that
deserializes modified JSON back. I believe that this is horribly wrong
and may have some security implications. This is out of the scope of
this patch. And I hope that somebody from the Zed team will provide the
reasoning behind this code.

Self-Review Checklist:

- [X] I've reviewed my own diff for quality, security, and reliability
- [X] Unsafe blocks (if any) have justifying comments
- [X] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [X] Tests cover the new/changed behavior
- [X] Performance impact has been considered and is acceptable

~~Closes #ISSUE~~ This pull request is also a bug report.

Release Notes:

- Fixed substitution of `${localEnv:VARNAME}` and
`${containerEnv:VARNAME}` in devcontainer.json when variable is not
defined
2026-04-20 16:12:17 -04:00
Kunall Banerjee
cd12d5f98d
dev_container: Make all PortAttributes fields optional (#53799)
The Dev Container [metadata
reference](https://containers.dev/implementors/json_reference/#port-attributes)
defines all five `PortAttributes` fields (`elevateIfNeeded`, `label`,
`onAutoForward`, `protocol`, `requireLocalPort`) as optional with
defined defaults. The struct required all of them, causing
deserialization failures for any `portsAttributes` entry that omitted a
field.

Add `#[serde(default)]` to each field, wrap `label` and `protocol` in
`Option<T>` (spec default: not set), and derive `Default` on
`OnAutoForward` with `Notify` as the default variant (spec default:
`notify`). The bool fields already default to false via
`serde(default)`, matching the spec.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Closes #53686.

Release Notes:

- Made all `PortAttributes` fields optional according to the Dev
Container spec
2026-04-20 10:05:12 -07:00
KyleBarton
17026b10d6
Use dockerfile build args correctly, respect context path, fix image aliasing (#54210)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #54193

Additionally, respects `cacheFrom` and `options` args defined in
devcontainer.json's `build` object. Also, addresses a bug which was
hiding these problems by overriding the primary build target with
aliasing.

Release Notes:

- Fixed an image aliasing but, respect build context and build args in
dev containers
2026-04-20 08:38:53 -07:00
KyleBarton
44c6ed117f
Use proper pathing for windows & nix in test (#54129)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes N/A

Release Notes:

- N/A
2026-04-16 16:33:55 -07:00
Bennet Bo Fenner
31e51613bd
Remove test_dockerfile_location_with_compose_context_parent test (#54119)
Removing it as it seems to constantly fail on CI on Windows. cc
@KyleBarton

```
FAIL [   0.693s] (1511/5475) dev_container devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent
  stdout ───

    running 1 test
    test devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent ... FAILED

    failures:

    failures:
        devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent

    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 62 filtered out; finished in 0.64s
    
  stderr ───

    thread 'devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent' (1992) panicked at crates\dev_container\src\devcontainer_manifest.rs:3817:9:
    assertion `left == right` failed
      left: None
     right: Some("C:\\\\path\\to\\local\\project\\.devcontainer/Dockerfile")
    stack backtrace:
       0: std::panicking::panic_handler
                 at /rustc/e408947bfd200af42db322daf0fadfe7e26d3bd1/library\std\src\panicking.rs:689
       1: core::panicking::panic_fmt
                 at /rustc/e408947bfd200af42db322daf0fadfe7e26d3bd1/library\core\src\panicking.rs:80
       2: core::panicking::assert_failed_inner
                 at /rustc/e408947bfd200af42db322daf0fadfe7e26d3bd1/library\core\src\panicking.rs:439
       3: core::panicking::assert_failed<enum2$<core::option::Option<std::path::PathBuf> >,enum2$<core::option::Option<std::path::PathBuf> > >
                 at C:\Rust\.rustup\toolchains\1.94.1-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\panicking.rs:394
       4: dev_container::devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent::__test_dockerfile_location_with_compose_context_parent::async_fn$0
                 at .\src\devcontainer_manifest.rs:3817
       5: gpui::executor::impl$4::block_test::async_block$0<tuple$<>,enum2$<dev_container::devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent::__test_dockerfile_location_with_compose_context_parent::async_fn_env$0> >
                 at C:\actions-runner\_work\zed\zed\crates\gpui\src\executor.rs:440
       6: scheduler::test_scheduler::impl$1::block
                 at C:\actions-runner\_work\zed\zed\crates\scheduler\src\test_scheduler.rs:538
       7: gpui::executor::ForegroundExecutor::block_test<tuple$<>,enum2$<dev_container::devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent::__test_dockerfile_location_with_compose_context_parent::async_fn_env$0> >
                 at C:\actions-runner\_work\zed\zed\crates\gpui\src\executor.rs:447
       8: dev_container::devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent::closure$0
                 at .\src\devcontainer_manifest.rs:3796
       9: gpui::test::run_test::closure$0
                 at C:\actions-runner\_work\zed\zed\crates\gpui\src\test.rs:109
      10: std::panicking::catch_unwind::do_call<gpui::test::run_test::closure_env$0,tuple$<> >
                 at C:\Rust\.rustup\toolchains\1.94.1-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:581
      11: alloc::vec::spec_from_iter::impl$0::from_iter<tuple$<gpui::app::entity_map::EntityId,alloc::boxed::Box<dyn$<core::any::Any>,alloc::alloc::Global> >,core::iter::adapters::filter_map::FilterMap<alloc::vec::drain::Drain<gpui::app::entity_map::EntityId,alloc::
      12: std::panicking::catch_unwind
                 at C:\Rust\.rustup\toolchains\1.94.1-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:544
      13: std::panic::catch_unwind<gpui::test::run_test::closure_env$0,tuple$<> >
                 at C:\Rust\.rustup\toolchains\1.94.1-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:359
      14: gpui::test::run_test
                 at C:\actions-runner\_work\zed\zed\crates\gpui\src\test.rs:106
      15: dev_container::devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent
                 at .\src\devcontainer_manifest.rs:3796
      16: dev_container::devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent::closure$0
                 at .\src\devcontainer_manifest.rs:3796
      17: core::ops::function::FnOnce::call_once<dev_container::devcontainer_manifest::test::test_dockerfile_location_with_compose_context_parent::closure_env$0,tuple$<> >
                 at C:\Rust\.rustup\toolchains\1.94.1-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:250
      18: core::ops::function::FnOnce::call_once
                 at /rustc/e408947bfd200af42db322daf0fadfe7e26d3bd1/library\core\src\ops\function.rs:250
    note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- x ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #ISSUE

Release Notes:

- N/A
2026-04-16 19:49:32 +00:00
Bing Wang
54f544430d
dev_container: Fix environment variables without an equals sign were treated as fatal parsing errors. (#53864)
in vscode the environment variables without an equals sign are ignored
https://github.com/devcontainers/cli/blob/main/src/spec-node/utils.ts#L488-L498
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53470

Release Notes:

- Fix environment variables without an equals sign were treated as fatal
parsing errors.
2026-04-16 19:12:32 +00:00
KyleBarton
5e6e41155e
Simplify remote workspace folder calculation for Dev Containers (#53829)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53635 

Release Notes:

- Fixed issues with Windows pathing when calculating target workspace
directory for dev containers
2026-04-16 11:50:08 -07:00
Toni Alatalo
bc88fe4931
dev_container: Resolve compose dockerfile path relative to build context (#53860)
## Context

When a Docker Compose service specifies `context: ..` and `dockerfile:
.devcontainer/Dockerfile`, Zed resolves the dockerfile path relative to
the compose file's directory instead of the build context. This produces
a doubled path like `.devcontainer/.devcontainer/Dockerfile` which
doesn't exist.

Per the [Docker Compose
spec](https://docs.docker.com/reference/compose-file/build/#dockerfile),
the `dockerfile` field is relative to the build context directory.

The fix resolves the context directory first (relative to the compose
file), then joins the dockerfile path to that.

Closes #53473

## Prior art

This fix is extracted from #53170 by @zdeneklapes, which addresses this
bug among several other dev container startup issues. This PR isolates
the dockerfile path resolution fix into a focused change to make it
easier to review and merge independently.

Differences from #53170:
- **Scope**: Only the dockerfile-relative-to-context fix, not the other
fixes (compose build args preservation, remote user fallback, Docker
inspect labels, etc.)
- **Implementation**: Inline resolution in `dockerfile_location()`
rather than separate helper methods
- **Absolute path handling**: Handles absolute dockerfile and context
paths
- **Tests**: Two test cases — compose file inside `.devcontainer/` with
`context: ..`, and compose file at project root with `context: .`

## How to Review

Single file change in
`crates/dev_container/src/devcontainer_manifest.rs`:
- **Fix** (line 234-252): Resolve build context relative to compose file
directory, then join dockerfile to that, instead of joining dockerfile
to `config_directory` directly. Uses `normalize_path` to resolve `..`
components.
- **Tests**: Two new `FakeDocker` compose config entries and
corresponding tests asserting correct resolved paths.

## Self-Review Checklist

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed docker-compose `dockerfile` path being resolved relative to the
compose file instead of the build `context` directory.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 09:56:32 -07:00
KyleBarton
7c45d93e5e
Use remote_user to execute on_create_command and update_content_command (#54020)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53995

Release Notes:

- Improved dev container remote script execution to more closely align
with the reference implementation
2026-04-16 09:00:41 -07:00
krisswee
75fa566511
dev_container: Detect actual buildx availability instead of assuming Docker has it (#53910)
`supports_compose_buildkit()` returned `!self.is_podman()`, assuming
every Docker install has buildx. On setups like Colima where the buildx
CLI plugin isn't installed, this causes builds to fail with "classic
builder doesn't support additional contexts" since `DOCKER_BUILDKIT=1`
alone isn't enough without the plugin.

Now probes for `docker buildx version` at construction time and caches
the result. When buildx isn't found, the existing scratch-image fallback
path (same one Podman uses) kicks in instead.

Closes #53890

Release Notes:

- Fixed dev container builds failing on Docker installations without the
buildx plugin.

---------

Co-authored-by: krisswee <krisswee@users.noreply.github.com>
Co-authored-by: KyleBarton <kjb@initialcapacity.io>
2026-04-16 08:44:48 -07:00
KyleBarton
c2736df3c7
Respect runArgs for dev containers (#53931)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53503

Release Notes:

- Fixed gap where runArgs were not respected when specified in
devcontainer.json
2026-04-15 08:24:34 -07:00
Sandro Meier
2d3f49e4b2
dev_container: Handle devcontainer.metadata label as JSON object or array (#53557)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Details

- The [devcontainer CLI writes the `devcontainer.metadata` label as a
bare JSON object](https://github.com/devcontainers/cli/issues/1054) when
there is only one metadata entry (e.g. docker-compose devcontainer with
a Dockerfile and no features)
- Zed's `deserialize_metadata` only accepted a JSON array, causing
deserialization to fail with `invalid type: map, expected a sequence`
- This made it impossible to attach to existing docker-compose
devcontainers created by the devcontainer CLI or VS Code

The fix tries parsing as an array first, then falls back to parsing as a
single object wrapped in a vec. This mirrors how the [devcontainer CLI
itself reads the
label](https://github.com/devcontainers/cli/blob/main/src/spec-node/imageMetadata.ts#L476-L493).

An upstream fix has also been submitted:
https://github.com/devcontainers/cli/pull/1199

## Reproduction

1. Create a docker-compose devcontainer with a Dockerfile and no
features:

`.devcontainer/devcontainer.json`:
```json
{
  "name": "repro",
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "remoteUser": "root"
}
```

`.devcontainer/docker-compose.yml`:
```yaml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    command: sleep infinity
    volumes:
      - ..:/workspace
```

`.devcontainer/Dockerfile`:
```dockerfile
FROM ubuntu:24.04
```

2. `devcontainer up --workspace-folder .`
3. Open the folder in Zed, fails with metadata deserialization error

Release Notes:

- Fixed attaching to a devcontainer that has a single metadata element
which was started with `devcontainer-cli`
2026-04-10 08:28:54 -07:00
Conrad Irwin
b8766ef3af
Use -- in more places that call subcommands with paths (#53484)
Thanks to Dario Weißer for the suggestion

Release Notes:

- Fixed some potential edge cases when paths in a project started
  with `-`.
2026-04-10 02:38:21 +00:00
KyleBarton
bd50418150
Fix dockerfile image and alias parsing (#53538)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #52928

Release Notes:

- Fixed handling of multi-stage and stage-specified dockerfiles in dev
container manifests
- Fixed the way we find the base image in a dev container when build
args need expansion
2026-04-09 13:27:29 -07:00
KyleBarton
358a796a94
Appropriately deserialize app_port in devcontainer.json (#53322)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53263

Properly deserializes the app_port field, even if it is an array of
strings. Also, removes app_port from consideration when building a
docker-compose-based dev container, since the spec specifies that it
only applies to non-docker-compose dev containers.

Release Notes:

- Fixed app_port deserialization in dev container
2026-04-07 10:58:35 -07:00
Peter Siegel
dee42503c7
dev_container: Preserve build context for docker-compose Dockerfiles (#53140)
When a Docker Compose service specifies a build context, the generated
override file was replacing it with an empty context directory. This
meant Dockerfiles that reference files relative to their build context
(e.g. `COPY . /app`) would fail. The fix preserves the original build
context from the compose service, falling back to the empty context
directory only when no context was specified.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed docker compose Dockerfile build context not being preserved in
dev_container integration.

---------

Co-authored-by: KyleBarton <kjb@initialcapacity.io>
2026-04-06 12:13:17 -07:00
Peter Siegel
909f6d12fc
dev_container: Make volumes key and source key in volume mounts optional (#53137)
Fixes some issues concerning volume mounts in the `dev_container`
integration:
1. Docker Compose services that don't define a volumes key cause
deserialization to fail because the field was required. This field is
not strictly necessary, i.e. for other services in a docker compose
devcontainer configuration which the editor is not attached to.
1. Volume mounts where source or target is absent (e.g. `tmpfs` mounts
that only need a target) also fail to parse because both fields were
required. This makes the source key optional, matching the Docker
Compose spec.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed devcontainer initialization erroneously requiring each service
to have a volumes key.
- Fixed devcontainer initialization erroneously requiring source keys
for all volume mounts.

---------

Co-authored-by: KyleBarton <kjb@initialcapacity.io>
2026-04-06 11:46:54 -07:00
Peter Siegel
4b1e0a30b8
dev_container: Parse env vars and docker labels with = in values correctly (#53134)
Fixes a parsing issue where docker env var key/value pairs can contain
an "=" character in the value. This is pretty common and present in all
[nvidia/cuda](https://hub.docker.com/r/nvidia/cuda) docker images. Also
adds some tests for env var parsing.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed a parsing failure where docker env var key/value pairs can
contain an "=" character in the value.
2026-04-06 11:30:23 -07:00
KyleBarton
c9d799e5e6
Ensure updateUID gets run for docker-compose and plain images (#53106)
Dev Containers should run a script which updates the remote UID of the
image user, so that files are still accessible. This was being run
incorrectly (on the Docker-compose side) or not at all (in the case of a
plain dev container image). This change fixes this

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53081

Release Notes:

- Fixed dev container behavior for configs which use images without a
dockerfile
2026-04-06 09:01:51 -07:00
KyleBarton
e9b280afe0
Account for windows absolute paths in bind mounts (#53093)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Addresses an auxiliary windows bug found in #52924 - bind mounts are not
working in Windows because MountDefinition is not accounting for
absolute Windows paths.

Release Notes:

- Fixed windows bind mount issue with dev containers
2026-04-03 14:55:12 -07:00
KyleBarton
5edb40c7a8
Use an object for docker compose ports rather than raw string (#53090)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53048

Release Notes:

- Fixed serialization error with Docker Compose for dev containers
2026-04-03 11:51:31 -07:00
Om Chillure
bde6a01068
Fix/devcontainer compose labels (#53057)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53042

Release Notes:

- Fixed dev container failing to open when Docker Compose file contains
`labels`

---------

Co-authored-by: KyleBarton <kjb@initialcapacity.io>
2026-04-03 16:31:18 +00:00
KyleBarton
3eadd41b5d
Dev containers native implementation (#52338)
## Context

Closes #11473

In-house Zed implementation of devcontainers. Replaces the dependency on
the [reference implementation](https://github.com/devcontainers/cli) via
Node.

This enables additional features with this implementation:
1. Zed extensions can be specified in the `customizations` block, via
this syntax in `devcontainer.json:
```
...
  "customizations": {
    "zed": {
      "extensions": ["vue", "ruby"],
    },
  },

```
2.
[forwardPorts](https://containers.dev/implementors/json_reference/#general-properties)
are supported for multiple ports proxied to the host

## How to Review

<!-- Help reviewers focus their attention:
- For small PRs: note what to focus on (e.g., "error handling in
foo.rs")
- For large PRs (>400 LOC): provide a guided tour — numbered list of
files/commits to read in order. (The `large-pr` label is applied
automatically.)
     - See the review process guidelines for comment conventions -->

## Self-Review Checklist

<!-- Check before requesting review: -->
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Improved devcontainer implementation by moving initialization and
creation in-house
2026-04-01 08:16:27 -07:00
Piotr Osiewicz
97421c670e
Remove unreferenced dev dependencies (#51093)
This will help with test times (in some cases), as nextest cannot figure
out whether a given rdep is actually an alive edge of the build graph

Closes #ISSUE

Before you mark this PR as ready for review, make sure that you have:
- [ ] Added a solid test coverage and/or screenshots from doing manual
testing
- [ ] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- N/A
2026-03-09 13:22:12 +01:00
KyleBarton
13933b88d5
Handle empty match list and do safe vector selection instead of slicing (#50232)
Handles a panic found in telemetry - when there are no template matches
or feature matches, the picker delegate will panic due to an indexing
exception. This solves by:
1. Returning early if there are no matches, and
2. Performing safe vector `get` operations instead of slicing, so we can
handle a not-found scenario.



https://github.com/user-attachments/assets/ac76164d-63aa-4e2b-8555-d8e4f4dd2524



Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- Fixed potential crash from initializing devcontainer.json
2026-02-26 17:06:24 +00:00
Jakub Konka
16dfc60ad2
util: Always use posix_spawn on macOS even with pre_exec hooks (#49090)
Here's some backstory:
* on macOS, @cole-miller and I noticed that since roughly Oct 2025, due
to some changes to latest macOS Tahoe, for any spawned child process we
needed to reset Mach exception ports
(https://github.com/zed-industries/zed/issues/36754 +
6e8f2d2ebe)
* the changes in that PR achieve that via `pre_exec` hook on
`std::process::Command` which then abandons `posix_spawn` syscall for
`fork` + `execve` dance on macOS (we tracked it down in Rust's std
implementation)
* as it turns out, `fork` + `execve` is pretty expensive on macOS
(apparently way more so than on other OSes like Linux) and `fork` takes
a process-wide lock on the allocator which is bad
* however, since we wanna reset exception ports on the child, the only
official way supported by Rust's std is to use `pre_exec` hook
* posix_spawn on macOS exposes this tho via a macOS specific extension
to that syscall `posix_spawnattr_setexceptionports_np` but there is no
way to use that via any standard interfaces in `std::process::Command`
* thus, it seemed like a good idea to instead create our own custom
Command wrapper that on non-macOS hosts is a zero-cost wrapper of
`smol::process::Command`, while on macOS we reimplement the minimum to
achieve `smol::process::Command`  with `posix_spawn` under-the-hood

Notably, this changeset improves git-blame in very large repos
significantly.

Release Notes:

- Fixed performance spawning child processes on macOS by always forcing
`posix_spawn` no matter what.

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2026-02-13 20:16:11 +01:00
Richard Feldman
ee3f40fe25
Re-add MultiWorkspace (#48800)
Release Notes:

- Added agent panel restoration. Now restarting your editor won't cause
your thread to be forgotten.

---------

Co-authored-by: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
Co-authored-by: Eric Holk <eric@zed.dev>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Anthony Eid <anthony@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Cameron Mcloughlin <cameron.studdstreet@gmail.com>
2026-02-12 01:06:23 +00:00
Oliver Azevedo Barnes
094122af92
devcontainer: Support .devcontainer.json in project root (#48814)
Closes #48683

Per the devcontainer spec, `.devcontainer.json` in the project root is
a valid config location. It is only used when no configurations are
found inside `.devcontainer/`.

Extract `find_configs_in_snapshot` for testability and add tests.

Release Notes:

- Added support for `.devcontainer.json` in project root
2026-02-10 11:42:26 -08:00
Finn Evers
165b404460
Revert "New multi workspace (#47795)" (#48776)
Preparing this just in case.

Release Notes:

- N/A
2026-02-09 15:37:16 +01:00
Mikayla Maki
1c21718587
New multi workspace (#47795)
It's happeningggggg

Release Notes:

- Changed the Agent Panel so that the Active Thread is restored on
restart.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Anthony Eid <anthony@zed.dev>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Richard Feldman <richard@zed.dev>
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-02-08 17:46:51 -08:00
KyleBarton
13fbbaeb59
Refer to devcontainer.js rather than devcontainer.cmd.js (#48387)
Closes #46947

When installed with `npm install -g`, devcontainer gets created as
`devcontainer.cmd`. However, when invoked as a script from node, it's
still `devcontainer.js`. This addresses that difference so that we don't
accidentally invoke `devcontainer.cmd.js`.

Release Notes:

- Fixed devcontainer operation on Windows for when the devcontainer cli
is not installed globally
2026-02-04 10:48:40 -08:00
KyleBarton
1dffb8c198
Simplify error message and provide a route to Zed log (#48301)
Closes #46780

Creates a better flow for handling errors when a devcontainer fails, by
shortening the message and giving the user a direct route to the Zed
log. Additionally, the error from `stderr` is printed with proper line
endings, making the log more legible

<img width="1716" height="1093" alt="Screenshot 2026-02-03 at 2 54
50 PM"
src="https://github.com/user-attachments/assets/08d7847b-c9b8-49e9-9936-6ae417f82fb2"
/>
<img width="1711" height="908" alt="Screenshot 2026-02-03 at 2 55 07 PM"
src="https://github.com/user-attachments/assets/a2676419-a118-432e-8e8a-32c6e92f4f3b"
/>
<img width="2901" height="542" alt="Screenshot 2026-02-03 at 2 55 48 PM"
src="https://github.com/user-attachments/assets/ea9de533-c1c6-4cb7-bd79-e44bd035537c"
/>


Release Notes:

- Improved error messaging and handling in the event of a devcontainer
launch failure
2026-02-03 23:07:10 +00:00
Caio Piccirillo
13a06e673b
Add detection of devcontainers in subfolders (#47411)
Release Notes:

- Add detection of devcontainers in subfolders

---------

Co-authored-by: KyleBarton <kjb@initialcapacity.io>
2026-02-03 17:05:40 +00:00
KyleBarton
85d03d5122
Use remote user from devcontainer CLI and respect shell in passwd (#48230)
Closes #46252

Uses the `remoteUser` property returned from the devcontainer CLI so
that settings are respected. Additionally, checks for a default shell in
`passwd` if `$SHELL` is not set.

Release Notes:

- Fixed remote_user and shell inconsistencies from within dev containers
2026-02-02 16:43:51 -08:00
KyleBarton
9569157743
Clean up error handling for some edge cases to prevent panic (#47513)
Release Notes:

- Improved error handling in the dev container crate to prevent panics
2026-01-23 13:22:08 -08:00
KyleBarton
c63a0bc358
Parse output from older version of the devcontainer CLI by looking for a JSON object in plaintext (#47403)
Closes #46852

The devcontainer CLI which ships with VS Code can be added to the user's
`PATH` with the VS Code command `Dev Containers: Install devcontainer
CLI`. This version of the CLI is older than what Zed was developed with,
and as a result, it doesn't separate its json-formatted output and its
plaintext metadata into distinct output streams. This broke parsing in
the CLI adapter in Zed. This fix accounts for that, and if parsing
fails, attempts to find a relevant JSON object in plaintext and tries to
parse that.

Tested with VSCode's version of the devcontainer CLI (`0.80.1`) as well
as the up-to-date version (`0.81.1`)

Release Notes:

- Improved parsing of devcontainer CLI output when using earlier
versions
2026-01-22 09:14:53 -08:00
KyleBarton
6acca17c44
Devcontainer setup modal (#47021)
Adds the ability to create a dev container definition from scratch.
Additionally, separates devcontainer logic out into its own crate, since
it was getting sufficiently complex to separate from the
`recent_projects` crate.

A screen recording of the modal experience:


https://github.com/user-attachments/assets/f6cf95e1-eb7b-4ca3-86c7-c1cbc26ca557


Release Notes:

- Added modal to initialize a dev container definition in the project
with `projects: initialize dev container`
- Added podman support for dev container actions with the `use_podman`
setting
- Improved devcontainer error handling

---------

Co-authored-by: Sam Coward <idoru42@gmail.com>
2026-01-16 15:20:48 -08:00