Skip to content

Commit 0d5a346

Browse files
authored
fix: use correct ref and path for features when useBuildContexts is true (#205)
1 parent 74a0aa5 commit 0d5a346

File tree

3 files changed

+14
-27
lines changed

3 files changed

+14
-27
lines changed

devcontainer/devcontainer.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ func (s *Spec) compileFeatures(fs billy.Filesystem, devcontainerDir, scratchDir
289289
if err != nil {
290290
return "", nil, fmt.Errorf("extract feature %s: %w", featureRefRaw, err)
291291
}
292-
fromDirective, directive, err := spec.Compile(featureName, containerUser, remoteUser, useBuildContexts, featureOpts)
292+
fromDirective, directive, err := spec.Compile(featureRef, featureName, featureDir, containerUser, remoteUser, useBuildContexts, featureOpts)
293293
if err != nil {
294294
return "", nil, fmt.Errorf("compile feature %s: %w", featureRefRaw, err)
295295
}

devcontainer/features/features.go

+4-11
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ func Extract(fs billy.Filesystem, devcontainerDir, directory, reference string)
162162
return nil, errors.New(`devcontainer-feature.json: name is required`)
163163
}
164164

165-
spec.Directory = directory
166165
return spec, nil
167166
}
168167

@@ -188,13 +187,11 @@ type Spec struct {
188187
Keywords []string `json:"keywords"`
189188
Options map[string]Option `json:"options"`
190189
ContainerEnv map[string]string `json:"containerEnv"`
191-
192-
Directory string `json:"-"`
193190
}
194191

195192
// Extract unpacks the feature from the image and returns a set of lines
196193
// that should be appended to a Dockerfile to install the feature.
197-
func (s *Spec) Compile(featureName, containerUser, remoteUser string, useBuildContexts bool, options map[string]any) (string, string, error) {
194+
func (s *Spec) Compile(featureRef, featureName, featureDir, containerUser, remoteUser string, useBuildContexts bool, options map[string]any) (string, string, error) {
198195
// TODO not sure how we figure out _(REMOTE|CONTAINER)_USER_HOME
199196
// as per the feature spec.
200197
// See https://containers.dev/implementors/features/#user-env-var
@@ -221,8 +218,8 @@ func (s *Spec) Compile(featureName, containerUser, remoteUser string, useBuildCo
221218
sort.Strings(runDirective)
222219
// See https://containers.dev/implementors/features/#invoking-installsh
223220
if useBuildContexts {
224-
fromDirective = "FROM scratch AS envbuilder_feature_" + featureName + "\nCOPY --from=" + featureName + " / /\n"
225-
runDirective = append([]string{"RUN", "--mount=type=bind,from=envbuilder_feature_" + featureName + ",target=/envbuilder-features/" + featureName + ",rw"}, runDirective...)
221+
fromDirective = "FROM scratch AS envbuilder_feature_" + featureName + "\nCOPY --from=" + featureRef + " / /\n"
222+
runDirective = append([]string{"RUN", "--mount=type=bind,from=envbuilder_feature_" + featureName + ",target=" + featureDir + ",rw"}, runDirective...)
226223
} else {
227224
runDirective = append([]string{"RUN"}, runDirective...)
228225
}
@@ -242,11 +239,7 @@ func (s *Spec) Compile(featureName, containerUser, remoteUser string, useBuildCo
242239
if comment != "" {
243240
lines = append(lines, comment)
244241
}
245-
if useBuildContexts {
246-
lines = append(lines, "WORKDIR /envbuilder-features/"+featureName)
247-
} else {
248-
lines = append(lines, "WORKDIR "+s.Directory)
249-
}
242+
lines = append(lines, "WORKDIR "+featureDir)
250243
envKeys := make([]string, 0, len(s.ContainerEnv))
251244
for key := range s.ContainerEnv {
252245
envKeys = append(envKeys, key)

devcontainer/features/features_test.go

+9-15
Original file line numberDiff line numberDiff line change
@@ -73,54 +73,48 @@ func TestCompile(t *testing.T) {
7373
t.Run("UnknownOption", func(t *testing.T) {
7474
t.Parallel()
7575
spec := &features.Spec{}
76-
_, _, err := spec.Compile("test", "containerUser", "remoteUser", false, map[string]any{
76+
_, _, err := spec.Compile("coder/test:latest", "test", "", "containerUser", "remoteUser", false, map[string]any{
7777
"unknown": "value",
7878
})
7979
require.ErrorContains(t, err, "unknown option")
8080
})
8181
t.Run("Basic", func(t *testing.T) {
8282
t.Parallel()
83-
spec := &features.Spec{
84-
Directory: "/",
85-
}
86-
_, directive, err := spec.Compile("test", "containerUser", "remoteUser", false, nil)
83+
spec := &features.Spec{}
84+
_, directive, err := spec.Compile("coder/test:latest", "test", "/", "containerUser", "remoteUser", false, nil)
8785
require.NoError(t, err)
8886
require.Equal(t, "WORKDIR /\nRUN _CONTAINER_USER=\"containerUser\" _REMOTE_USER=\"remoteUser\" ./install.sh", strings.TrimSpace(directive))
8987
})
9088
t.Run("ContainerEnv", func(t *testing.T) {
9189
t.Parallel()
9290
spec := &features.Spec{
93-
Directory: "/",
9491
ContainerEnv: map[string]string{
9592
"FOO": "bar",
9693
},
9794
}
98-
_, directive, err := spec.Compile("test", "containerUser", "remoteUser", false, nil)
95+
_, directive, err := spec.Compile("coder/test:latest", "test", "/", "containerUser", "remoteUser", false, nil)
9996
require.NoError(t, err)
10097
require.Equal(t, "WORKDIR /\nENV FOO=bar\nRUN _CONTAINER_USER=\"containerUser\" _REMOTE_USER=\"remoteUser\" ./install.sh", strings.TrimSpace(directive))
10198
})
10299
t.Run("OptionsEnv", func(t *testing.T) {
103100
t.Parallel()
104101
spec := &features.Spec{
105-
Directory: "/",
106102
Options: map[string]features.Option{
107103
"foo": {
108104
Default: "bar",
109105
},
110106
},
111107
}
112-
_, directive, err := spec.Compile("test", "containerUser", "remoteUser", false, nil)
108+
_, directive, err := spec.Compile("coder/test:latest", "test", "/", "containerUser", "remoteUser", false, nil)
113109
require.NoError(t, err)
114110
require.Equal(t, "WORKDIR /\nRUN FOO=\"bar\" _CONTAINER_USER=\"containerUser\" _REMOTE_USER=\"remoteUser\" ./install.sh", strings.TrimSpace(directive))
115111
})
116112
t.Run("BuildContext", func(t *testing.T) {
117113
t.Parallel()
118-
spec := &features.Spec{
119-
Directory: "/",
120-
}
121-
fromDirective, runDirective, err := spec.Compile("test", "containerUser", "remoteUser", true, nil)
114+
spec := &features.Spec{}
115+
fromDirective, runDirective, err := spec.Compile("coder/test:latest", "test", "/.envbuilder/feature/test-d8e8fc", "containerUser", "remoteUser", true, nil)
122116
require.NoError(t, err)
123-
require.Equal(t, "FROM scratch AS envbuilder_feature_test\nCOPY --from=test / /", strings.TrimSpace(fromDirective))
124-
require.Equal(t, "WORKDIR /envbuilder-features/test\nRUN --mount=type=bind,from=envbuilder_feature_test,target=/envbuilder-features/test,rw _CONTAINER_USER=\"containerUser\" _REMOTE_USER=\"remoteUser\" ./install.sh", strings.TrimSpace(runDirective))
117+
require.Equal(t, "FROM scratch AS envbuilder_feature_test\nCOPY --from=coder/test:latest / /", strings.TrimSpace(fromDirective))
118+
require.Equal(t, "WORKDIR /.envbuilder/feature/test-d8e8fc\nRUN --mount=type=bind,from=envbuilder_feature_test,target=/.envbuilder/feature/test-d8e8fc,rw _CONTAINER_USER=\"containerUser\" _REMOTE_USER=\"remoteUser\" ./install.sh", strings.TrimSpace(runDirective))
125119
})
126120
}

0 commit comments

Comments
 (0)