config: substitute variables in feature-contributed metadata#53
Conversation
Feature-supplied mounts, env, and lifecycle commands now get host-context
substitution. Previously only the user's devcontainer.json fields were
resolved by ResolveBytes; feature/base-image metadata folded in by
MergeMetadata kept their literal ${devcontainerId}, ${localWorkspaceFolder},
${localEnv:*} tokens and reached ContainerCreate unchanged, causing Docker
to reject mounts like dind-var-lib-docker-${devcontainerId} as having
invalid volume name characters.
MergeMetadata now accepts a SubstitutionContext and resolves layer-
contributed strings before folding them into cfg. Callers (Up's
applyMetadataMerge, Attach) build the context from cfg + localEnv.
Fixes #52.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (3)
📝 WalkthroughWalkthroughAdds placeholder substitution during MergeMetadata by accepting a SubstitutionContext, substituting layer string fields before merging, and threading localEnv through attach/up. Validated by updated unit tests and an integration test. ChangesFeature mount variable substitution
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
up.go (1)
263-277:⚠️ Potential issue | 🟠 Major | ⚡ Quick winNormalize
localEnvbefore metadata substitution in Up paths.
applyMetadataMergecurrently usesopts.LocalEnvas-is. When nil,${localEnv:*}from feature/base metadata will not resolve from host env, which conflicts with theUpOptions.LocalEnvcontract (nil => useos.Environ()) and differs fromAttachWithbehavior.Suggested fix
func applyMetadataMerge(cfg *config.ResolvedConfig, baseLayers []config.FeatureMetadata, localEnv map[string]string) { + if localEnv == nil { + localEnv = environAsMap(os.Environ()) + } chain := make([]config.FeatureMetadata, 0, len(baseLayers)+len(cfg.Features)) chain = append(chain, baseLayers...) for _, f := range cfg.Features { if f.AlreadyInstalled { continue } chain = append(chain, f.Metadata) } subCtx := config.SubstitutionContext{ LocalWorkspaceFolder: cfg.LocalWorkspaceFolder, ContainerWorkspaceFolder: cfg.ContainerWorkspaceFolder, DevcontainerID: cfg.DevcontainerID, LocalEnv: localEnv, } config.MergeMetadata(cfg, subCtx, chain) cfg.Finalize() }Also applies to: 475-475, 863-879
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f31bd3dd-e0b5-473a-af10-4790707b6cba
📒 Files selected for processing (5)
attach.goconfig/merge_metadata.goconfig/merge_metadata_test.gotest/integration/feature_mount_substitution_test.goup.go
After `if x == nil { t.Fatal(...) }`, staticcheck doesn't model
t.Fatal/t.Fatalf as terminating, so subsequent dereferences trip
SA5011. Add an explicit return so the analyzer sees the post-nil
code as unreachable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Fix #52: feature-contributed mounts (and env, lifecycle commands, …) now get host-context substitution before reaching Docker.
Previously
ResolveBytesonly substituted variables in the user'sdevcontainer.json. Fields folded in afterwards byMergeMetadatafrom feature / base-image metadata kept their literal\${devcontainerId},\${localWorkspaceFolder},\${localEnv:*}tokens and flowed straight intoContainerCreate. The docker-in-docker feature'sdind-var-lib-docker-\${devcontainerId}volume mount tripped Docker's volume name validator.MergeMetadatanow takes aSubstitutionContextand resolves layer-contributed strings before folding them in. Substitution copies — input layers are not mutated, so cached feature metadata stays clean. Call sites (applyMetadataMergeinup.go,Attach) build the context fromcfg+localEnv.Test plan
TestMergeMetadata_SubstitutesLayerStringscovers mount sources, env values, and lifecycle commands with\${devcontainerId}/\${localWorkspaceFolder}/\${localEnv:*}TestMergeMetadata_SubstitutionDoesNotMutateLayerspins the no-input-mutation invariant (feature metadata is often shared / cached)integration, requires Docker):TestFeatureMountSubstitutionreproduces the docker-in-docker mount shape with a local feature, assertsUpsucceeds and the inspected container mount path embeds the substituted volume name🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests