Skip to content

fix(cli): tag an image's Features build per workspace in up - #72

Merged
aledbf merged 2 commits into
mainfrom
cabundle-e2e-isolation
Nov 13, 2026
Merged

aledbf merged 2 commits into
mainfrom
cabundle-e2e-isolation

Conversation

@aledbf

@aledbf aledbf commented Nov 13, 2026

Copy link
Copy Markdown
Member

Cause

TestE2E_CABundle/container failed when another run of it shared the Docker daemon: up ran a postCreateCommand pointing at the other run's TLS server and nonce.

It isn't the workspace basename. folderImageName hashes the full path (vsc-<basename>-<sha256(path)>), the same as TS getFolderImageName, and t.TempDir paths differ between runs. The collision comes from the product: up of an image configuration with Features called extendImageWithFeatures with no names, so it tagged the build <image>-features, for example mcr.microsoft.com/devcontainers/base:ubuntu-features. Every workspace on that image shared the tag. One run's build retagged it before the other ran docker run, so the container started from the wrong image and its metadata label carried the wrong lifecycle commands.

TS tags it ${getFolderImageName(common)}-features (containerFeatures.ts, extendImage), and our build already uses folder+"-features". Only up diverged.

Fix

  • fix(cli): fromImage passes folderImageName(workspaceFolder) + "-features", which matches TS. The hermetic regression test TestUpImageFeaturesTagsFolderImage fails before the change (tags = [alpine:3-features]) and passes after. There's a migration note in docs/migration.md because image tags are a public contract.

  • test(cli): three other e2e tests could collide under concurrency:

    • TestE2E_ComposeBuildLabel: Compose takes the project and image name from the workspace basename, and t.TempDir makes it 001 in every run.
    • TestE2E_SiblingDockerignore: used the fixed tag e2e-dockerignore:test, which another run's cleanup could remove.
    • TestE2E_BuildUpExec_*: used the repo fixture folder directly, so runs from one checkout shared a container (both match it by its local_folder label).

    Workspaces now get a unique basename (ws-<random>), the fixtures are copied into one, and the dockerignore image name is unique.

Evidence

Two concurrent TestE2E_CABundle runs (separate TMPDIR):

  • Before: reproduced on the first attempt. Run 1 (server 172.17.0.5, nonce 03cedd…) failed in container with postCreateCommand … https://172.17.0.8:8443/5640088059827986, which is run 2's server and nonce. Both logs show naming to mcr.microsoft.com/devcontainers/base:ubuntu-features.
  • After: both pass, tagging vsc-ws-25330079-eb42…-features and vsc-ws-2525456359-b1dd…-features.

Two concurrent full -run '^TestE2E' suites after the change: both pass.

Validation (local)

  • go vet ./internal/cli/, go tool task lint: pass.
  • go tool task test:race: pass with TMPDIR=/dev/shm/…. With the default /tmp, TestOpen* and TestComputeWorkspaceConfigOutsideARepository fail before and after this change, because this machine has a stray empty /tmp/.git.
  • go tool task test:e2e: all 13 pass.
  • go tool task parity:contract: 114 matched. Two read-configuration.features-configuration-local-feature-* cases fail identically without this change, for local environment reasons (the same /tmp/.git, or unnormalized paths when TMPDIR is outside /tmp).
  • PARITY_COMMAND=up task parity:runtime: up.image-with-features-success and up.image-with-git-feature-success matched. The 22 failures don't go through this path; they're /tmp/parity-* workspaces under the stray /tmp/.git plus cleanup timeouts. I'm leaving runtime parity to CI.

🤖 Generated with Claude Code

aledbf and others added 2 commits November 13, 2026 01:01
`up` of an image configuration with Features tagged the build
`<image>-features`, so every workspace on the same image shared one
tag. Concurrent runs retagged it under each other and could start a
container from another workspace's image, with that workspace's
lifecycle commands in its metadata label.

The official CLI tags it `vsc-<folder>-<hash>-features`
(getFolderImageName in extendImage), and `build` already did; `up` now
does too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Compose names its project and images after the workspace's basename,
which t.TempDir makes 001 in every run; the dockerignore test used a
fixed tag; and the build/up/exec tests used the repository's fixture
folder, so runs from one checkout shared a container. Workspaces now
have a unique basename, the fixtures are copied into one, and the
dockerignore image name is unique.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@aledbf
aledbf merged commit 6de1874 into main Nov 13, 2026
13 checks passed
@aledbf
aledbf deleted the cabundle-e2e-isolation branch November 13, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant