@@ -31,6 +31,7 @@ var imagetoolsTests = []func(t *testing.T, sb integration.Sandbox){
3131 testImagetoolsCopyIndex ,
3232 testImagetoolsInspectAndFilter ,
3333 testImagetoolsCreatePlatformFilter ,
34+ testImagetoolsAppend ,
3435 testImagetoolsAnnotation ,
3536 testImagetoolsMergeSources ,
3637 testImagetoolsMergeSourcesWithAttestations ,
@@ -332,6 +333,71 @@ func testImagetoolsCreatePlatformFilter(t *testing.T, sb integration.Sandbox) {
332333 require .Equal (t , 1 , attestationCount )
333334}
334335
336+ // testImagetoolsAppend verifies create --append adds a new source onto an
337+ // existing target image and rewrites it as a combined index.
338+ func testImagetoolsAppend (t * testing.T , sb integration.Sandbox ) {
339+ if ! isDockerContainerWorker (sb ) {
340+ t .Skip ("only testing with docker-container worker, imagetools only runs on docker-container" )
341+ }
342+
343+ dir := createDockerfileWithArches (t , "amd64" , "arm64" )
344+ registry , err := sb .NewRegistry ()
345+ if errors .Is (err , integration .ErrRequirements ) {
346+ t .Skip (err .Error ())
347+ }
348+ require .NoError (t , err )
349+
350+ target := registry + "/buildx/imtools-append-target:latest"
351+ out , err := buildCmd (sb , withArgs ("-t" , target , "--push" , "--platform=linux/amd64" , "--provenance=false" , dir ))
352+ require .NoError (t , err , string (out ))
353+
354+ cmd := buildxCmd (sb , withArgs ("imagetools" , "inspect" , target , "--raw" ))
355+ dt , err := cmd .CombinedOutput ()
356+ require .NoError (t , err , string (dt ))
357+
358+ var amd64Manifest ocispecs.Manifest
359+ err = json .Unmarshal (dt , & amd64Manifest )
360+ require .NoError (t , err )
361+ require .Equal (t , images .MediaTypeDockerSchema2Manifest , amd64Manifest .MediaType )
362+ amd64Digest := digest .FromBytes (dt )
363+
364+ source := registry + "/buildx/imtools-append-source:latest"
365+ out , err = buildCmd (sb , withArgs ("-t" , source , "--push" , "--platform=linux/arm64" , "--provenance=false" , dir ))
366+ require .NoError (t , err , string (out ))
367+
368+ cmd = buildxCmd (sb , withArgs ("imagetools" , "inspect" , source , "--raw" ))
369+ dt , err = cmd .CombinedOutput ()
370+ require .NoError (t , err , string (dt ))
371+
372+ var arm64Manifest ocispecs.Manifest
373+ err = json .Unmarshal (dt , & arm64Manifest )
374+ require .NoError (t , err )
375+ require .Equal (t , images .MediaTypeDockerSchema2Manifest , arm64Manifest .MediaType )
376+ arm64Digest := digest .FromBytes (dt )
377+
378+ cmd = buildxCmd (sb , withArgs ("imagetools" , "create" , "--append" , "-t" , target , source ))
379+ dt , err = cmd .CombinedOutput ()
380+ require .NoError (t , err , string (dt ))
381+
382+ cmd = buildxCmd (sb , withArgs ("imagetools" , "inspect" , target , "--raw" ))
383+ dt , err = cmd .CombinedOutput ()
384+ require .NoError (t , err , string (dt ))
385+
386+ var idx ocispecs.Index
387+ err = json .Unmarshal (dt , & idx )
388+ require .NoError (t , err )
389+ require .Equal (t , images .MediaTypeDockerSchema2ManifestList , idx .MediaType )
390+ require .Len (t , idx .Manifests , 2 )
391+
392+ platformsByDigest := map [digest.Digest ]string {}
393+ for _ , desc := range idx .Manifests {
394+ require .NotNil (t , desc .Platform )
395+ platformsByDigest [desc .Digest ] = platforms .Format (* desc .Platform )
396+ }
397+ require .Equal (t , "linux/amd64" , platformsByDigest [amd64Digest ])
398+ require .Equal (t , "linux/arm64" , platformsByDigest [arm64Digest ])
399+ }
400+
335401// testImagetoolsAnnotation verifies index and manifest annotations added by imagetools create.
336402func testImagetoolsAnnotation (t * testing.T , sb integration.Sandbox ) {
337403 if ! isDockerContainerWorker (sb ) {
0 commit comments