@@ -149,6 +149,97 @@ USER 1000`, params.DockerfileContent)
149149 })
150150}
151151
152+ func TestCompileWithFeaturesOverrideInstallOrder (t * testing.T ) {
153+ t .Parallel ()
154+ registry := registrytest .New (t )
155+ featureOne := registrytest .WriteContainer (t , registry , emptyRemoteOpts , "coder/one:tomato" , features .TarLayerMediaType , map [string ]any {
156+ "install.sh" : "hey" ,
157+ "devcontainer-feature.json" : features.Spec {
158+ ID : "one" ,
159+ Version : "tomato" ,
160+ Name : "One" ,
161+ },
162+ })
163+ featureTwo := registrytest .WriteContainer (t , registry , emptyRemoteOpts , "coder/two:potato" , features .TarLayerMediaType , map [string ]any {
164+ "install.sh" : "hey" ,
165+ "devcontainer-feature.json" : features.Spec {
166+ ID : "two" ,
167+ Version : "potato" ,
168+ Name : "Two" ,
169+ },
170+ })
171+ featureThree := registrytest .WriteContainer (t , registry , emptyRemoteOpts , "coder/three:apple" , features .TarLayerMediaType , map [string ]any {
172+ "install.sh" : "hey" ,
173+ "devcontainer-feature.json" : features.Spec {
174+ ID : "three" ,
175+ Version : "apple" ,
176+ Name : "Three" ,
177+ },
178+ })
179+
180+ featureOneMD5 := md5 .Sum ([]byte (featureOne ))
181+ featureOneDir := fmt .Sprintf ("/.envbuilder/features/one-%x" , featureOneMD5 [:4 ])
182+ featureTwoMD5 := md5 .Sum ([]byte (featureTwo ))
183+ featureTwoDir := fmt .Sprintf ("/.envbuilder/features/two-%x" , featureTwoMD5 [:4 ])
184+ featureThreeMD5 := md5 .Sum ([]byte (featureThree ))
185+ featureThreeDir := fmt .Sprintf ("/.envbuilder/features/three-%x" , featureThreeMD5 [:4 ])
186+
187+ t .Run ("OverrideReverseOrder" , func (t * testing.T ) {
188+ // featureThree then featureTwo are explicitly ordered first; featureOne
189+ // is unconstrained and falls to the alphabetical remainder.
190+ raw := `{
191+ "image": "localhost:5000/envbuilder-test-ubuntu:latest",
192+ "features": {
193+ "` + featureOne + `": {},
194+ "` + featureTwo + `": {},
195+ "` + featureThree + `": {}
196+ },
197+ "overrideFeatureInstallOrder": ["` + featureThree + `", "` + featureTwo + `"]
198+ }`
199+ dc , err := devcontainer .Parse ([]byte (raw ))
200+ require .NoError (t , err )
201+ fs := memfs .New ()
202+
203+ params , err := dc .Compile (fs , "" , workingDir , "" , "" , false , stubLookupEnv )
204+ require .NoError (t , err )
205+
206+ // featureThree and featureTwo come first (in override order),
207+ // then featureOne last (alphabetical remainder).
208+ require .Contains (t , params .DockerfileContent , "WORKDIR " + featureThreeDir + "\n " )
209+ require .Contains (t , params .DockerfileContent , "WORKDIR " + featureTwoDir + "\n " )
210+ require .Contains (t , params .DockerfileContent , "WORKDIR " + featureOneDir + "\n " )
211+
212+ threeIdx := strings .Index (params .DockerfileContent , "WORKDIR " + featureThreeDir )
213+ twoIdx := strings .Index (params .DockerfileContent , "WORKDIR " + featureTwoDir )
214+ oneIdx := strings .Index (params .DockerfileContent , "WORKDIR " + featureOneDir )
215+ require .Less (t , threeIdx , twoIdx , "three should be installed before two" )
216+ require .Less (t , twoIdx , oneIdx , "two should be installed before one" )
217+ })
218+
219+ t .Run ("UnknownOverrideEntryIgnored" , func (t * testing.T ) {
220+ // An entry in overrideFeatureInstallOrder that doesn't match any
221+ // feature key should be silently ignored.
222+ raw := `{
223+ "image": "localhost:5000/envbuilder-test-ubuntu:latest",
224+ "features": {
225+ "` + featureOne + `": {},
226+ "` + featureTwo + `": {}
227+ },
228+ "overrideFeatureInstallOrder": ["does-not-exist", "` + featureTwo + `"]
229+ }`
230+ dc , err := devcontainer .Parse ([]byte (raw ))
231+ require .NoError (t , err )
232+ fs := memfs .New ()
233+
234+ params , err := dc .Compile (fs , "" , workingDir , "" , "" , false , stubLookupEnv )
235+ require .NoError (t , err )
236+
237+ twoIdx := strings .Index (params .DockerfileContent , "WORKDIR " + featureTwoDir )
238+ oneIdx := strings .Index (params .DockerfileContent , "WORKDIR " + featureOneDir )
239+ require .Less (t , twoIdx , oneIdx , "two should be installed before one" )
240+ })
241+ }
242+
152243func TestCompileDevContainer (t * testing.T ) {
153244 t .Parallel ()
154245 t .Run ("WithImage" , func (t * testing.T ) {
0 commit comments