@@ -313,6 +313,33 @@ func (d *ociImageDestination) addManifest(desc *imgspecv1.Descriptor) {
313313 d .index .Manifests = append (slices .Clone (d .index .Manifests ), * desc )
314314}
315315
316+ // addSignatureManifest is similar to addManifest, but replace the entry based on imgspecv1.AnnotationRefName
317+ // and returns the old digest to delete it later.
318+ func (d * ociImageDestination ) addSignatureManifest (desc * imgspecv1.Descriptor ) (* imgspecv1.Descriptor , error ) {
319+ if desc .Annotations == nil || desc .Annotations [imgspecv1 .AnnotationRefName ] == "" {
320+ return nil , errors .New ("cannot add signature manifest without ref.name" )
321+ }
322+ for i , m := range d .index .Manifests {
323+ if m .Annotations [imgspecv1 .AnnotationRefName ] == desc .Annotations [imgspecv1 .AnnotationRefName ] {
324+ // Replace it completely.
325+ oldDesc := d .index .Manifests [i ]
326+ d .index .Manifests [i ] = * desc
327+ return & oldDesc , nil
328+ }
329+ }
330+ // It shouldn't happen, but if there's no entry with the same ref name, but the same digest, just replace it.
331+ for i , m := range d .index .Manifests {
332+ if m .Digest == desc .Digest && m .Annotations [imgspecv1 .AnnotationRefName ] == "" {
333+ // Replace it completely.
334+ d .index .Manifests [i ] = * desc
335+ return nil , nil
336+ }
337+ }
338+ // It's a new entry to be added to the index. Use slices.Clone() to avoid a remote dependency on how d.index was created.
339+ d .index .Manifests = append (slices .Clone (d .index .Manifests ), * desc )
340+ return nil , nil
341+ }
342+
316343// CommitWithOptions marks the process of storing the image as successful and asks for the image to be persisted.
317344// WARNING: This does not have any transactional semantics:
318345// - Uploaded data MAY be visible to others before CommitWithOptions() is called
@@ -324,7 +351,7 @@ func (d *ociImageDestination) CommitWithOptions(ctx context.Context, options pri
324351 if err != nil {
325352 return err
326353 }
327- // Delete unreferenced blobs (e.g. old signature manifest config)
354+ // Delete unreferenced blobs (e.g. old signature manifest and its config)
328355 if ! d .blobsToDelete .Empty () {
329356 count := make (map [digest.Digest ]int )
330357 err = d .ref .countBlobsReferencedByIndex (count , & d .index , d .sharedBlobDir )
@@ -471,15 +498,26 @@ func (d *ociImageDestination) putSignaturesToSigstoreAttachment(ctx context.Cont
471498 if err != nil {
472499 return err
473500 }
474- d . addManifest (& imgspecv1.Descriptor {
501+ oldDesc , err := d . addSignatureManifest (& imgspecv1.Descriptor {
475502 MediaType : signManifest .MediaType ,
476503 Digest : signDigest ,
477504 Size : int64 (len (signManifestBlob )),
478505 Annotations : map [string ]string {
479506 imgspecv1 .AnnotationRefName : signTag ,
480507 },
481508 })
482-
509+ if err != nil {
510+ return err
511+ }
512+ // If it overwrote an existing signature manifest, delete blobs referenced by the old manifest.
513+ if oldDesc != nil {
514+ referencedBlobs := make (map [digest.Digest ]int )
515+ err = d .ref .countBlobsForDescriptor (referencedBlobs , oldDesc , d .sharedBlobDir )
516+ if err != nil {
517+ return fmt .Errorf ("error counting blobs for digest %s: %w" , oldDesc .Digest .String (), err )
518+ }
519+ d .blobsToDelete .AddSeq (maps .Keys (referencedBlobs ))
520+ }
483521 return nil
484522}
485523
0 commit comments