feat(compose): use compose project name as workspace name - #1006
feat(compose): use compose project name as workspace name#1006devsy-app[bot] wants to merge 3 commits into
Conversation
Resolve the compose project name following the @devcontainers/cli reference precedence (shell COMPOSE_PROJECT_NAME, .env COMPOSE_PROJECT_NAME, top-level compose name) instead of always using the sanitized random runner id, so an existing compose project is reused. The devcontainer.json name field is not used; it is a UI display name per the spec.
✅ Deploy Preview for devsydev canceled.
|
✅ Deploy Preview for images-devsy-sh canceled.
|
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 93 |
| Duplication | 4 |
AI Reviewer: run a review on demand. To trigger the first review automatically, go to your organization or repository integration settings. AI can make mistakes. Always validate suggestions.
TIP This summary will be updated as you push new changes.
|
If you're new to commit signing, there are different ways to set it up: Sign commits with
|
6db5734 to
4962a7a
Compare
Add e2e specs asserting the com.docker.compose.project label reflects the resolved project name (top-level compose name, COMPOSE_PROJECT_NAME from .env, shell COMPOSE_PROJECT_NAME) and that a second devsy up reuses the existing compose project and container. The dev container is located by devsy's dev.containers.id label for these project-name assertions, since the project name under test is not guaranteed to equal the sanitized workspace UID. The shared findComposeContainer helper is updated to resolve the actual compose project name from the dev container's labels (rather than assuming the sanitized workspace UID) before querying by project + service, so it stays correct now that devsy resolves the project name from COMPOSE_PROJECT_NAME / .env / the top-level compose name. This keeps the existing multi-service, shutdown, v2-features, and updateRemoteUserUID specs green alongside the new ones. The .env-backed case writes the workspace .env at runtime because .env is gitignored and thus not part of the committed testdata.
Resolves #999.
Summary
When running
devsy workspace up .with the docker provider on a compose-based devcontainer, devsy ignored the configured compose project name and always used the sanitized random runner id, spawning a separate compose project instead of reusing an existing one. This PR resolves the project name following the@devcontainers/clireference precedence so an existing compose project is reused.Precedence (final, aligned with the reference)
COMPOSE_PROJECT_NAMEfrom the shell session.COMPOSE_PROJECT_NAMEfrom the collected.envfiles (first definition wins). devsy passes these same files todocker compose --env-file, so readingCOMPOSE_PROJECT_NAMEfrom the same set keeps devsy and the runtime compose invocation consistent.name:(last declaration wins, matching multi-file override semantics).devsy passes the resolved name via
--project-name, which is Docker Compose's highest-precedence override — exactly the mechanism devsy was already using, just with the wrong value.Comparison with the official specification
containers.dev): thenamefield is "A name for the dev container displayed in the UI" — a display name, not a compose project name. The spec's Docker Compose section is silent on project-name derivation. Sodevcontainer.jsonnameis not used.@devcontainers/clireference (getProjectNameinsrc/spec-node/dockerCompose.ts): resolvesCOMPOSE_PROJECT_NAME(shell) →COMPOSE_PROJECT_NAME(<cwd>/.env) → top-levelname:(with a guard distinguishing a user-declaredname:from compose-go's injected default) → directory basename. devsy matches this ladder, using the random runner id as its final fallback to preserve existing behavior.docs.docker.com/compose/how-tos/project-name/):-pflag >COMPOSE_PROJECT_NAMEenv > top-levelname:> project-dir basename > current-dir basename. Since devsy passes the resolved name via--project-name, step 1 (the-pflag) is what takes effect.TopLevelComposeNamereads raw compose fragments, so it only returns user-declared names — never compose-go's injectedname: devcontainerdefault, which is the same distinction the reference's fragment re-check makes.Known divergence from the reference
devsy reads
COMPOSE_PROJECT_NAMEfrom.devcontainer/.envand compose-dir.envfiles in addition to<workspace>/.env, whereas the reference reads only<workspace>/.env. This is intentional: devsy already passes all of these as--env-filetodocker compose, so they are part of compose's own resolution at runtime. Reading from the same set keeps the computed name and the runtime name identical, which is what makes "reuse the existing compose project" work.Changes
pkg/devcontainer/compose.go:loadComposeProjectno longer unconditionally overwrites the project name with the random runner id; it calls the newresolveComposeProjectName.resolveComposeProjectNameimplements the precedence above.baseEnvFiles(devcontainer config-dir.env+ workspace-root.env, used to resolve$COMPOSE_FILEandCOMPOSE_PROJECT_NAME) andcomposeDirEnvFiles(.envfiles colocated with the resolved compose files, added after compose-file resolution). Deduplication preserves first-seen order.pkg/compose/projectname.go(new): pure, testable helpersProjectNameFromEnvFilesandTopLevelComposeName, plus theComposeProjectNameEnvconstant.pkg/compose/helper.go: addedSanitizeProjectName(exported wrapper around the existing sanitization logic) so the devcontainer layer can sanitize the random-id fallback without re-implementing version-dependent character rules.Notes
stopDockerCompose/deleteDockerComposeare unaffected: they derive the project name from the running container'scom.docker.compose.projectlabel, which compose sets from--project-nameatuptime, so they stay consistent with the new resolution.Namefrom compose-go is intentionally not trusted directly, because compose-go falls back to the project directory's base name when no explicit name source is present. Each higher-precedence source is read explicitly instead.Testing
ProjectNameFromEnvFiles,TopLevelComposeName, andSanitizeProjectNameinpkg/compose/projectname_test.go.resolveComposeProjectName(all precedence levels, multi-file last-wins),baseEnvFiles(collection + dedup),composeDirEnvFiles, andappendEnvFilesinpkg/devcontainer/compose_projectname_test.go.go vet,gofmt,golines, andgolangci-lint v2.12.2(--new-from-rev=origin/main) all clean;go build ./...passes.Test up-docker-composesuite is green in CI, including four new specs that assert the resolved project name (top-level composename:,COMPOSE_PROJECT_NAMEfrom.env,COMPOSE_PROJECT_NAMEfrom the shell, and reuse of the existing compose project on a secondup). The sharedfindComposeContainerhelper now resolves the actual compose project name from the dev container labels (rather than assuming the sanitized workspace UID) so the existing multi-service, shutdown, v2-features, andupdateRemoteUserUIDspecs stay green alongside the new ones.Test up-workspaces(git exit-128) andTest self-update(update --dry-runnetwork call failing instantly at 0.08s) — both environmental flakes unrelated to this change (this PR only touches compose project-name resolution and theup-docker-composehelper; neither test exercises compose). They passed on the prior commit on this same branch and fail from network/git-runner conditions, not from these code changes.This PR was created by an AI agent (OpenHands) on behalf of the user.