Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: list output handler #1176

Merged
merged 16 commits into from
Feb 26, 2025
6 changes: 6 additions & 0 deletions cmd/notation/internal/display/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,9 @@ func NewVerifyHandler(printer *output.Printer) metadata.VerifyHandler {
func NewBlobVerifyHandler(printer *output.Printer) metadata.BlobVerifyHandler {
return text.NewBlobVerifyHandler(printer)
}

// NewListHandler creates a new metadata ListHandler for rendering signature
// metadata information in a tree format.
func NewListHandler(printer *output.Printer) metadata.ListHandler {
return tree.NewListHandler(printer)
}
20 changes: 20 additions & 0 deletions cmd/notation/internal/display/metadata/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,23 @@ type BlobVerifyHandler interface {
// outcomes must not be nil or empty.
OnVerifySucceeded(outcomes []*notation.VerificationOutcome, blobPath string)
}

// ListHandler is a handler for rendering metadata information of a list of
// signatures.
type ListHandler interface {
Renderer

// OnResolvingTagReference outputs the tag reference warning.
OnResolvingTagReference(reference string)

// OnReferenceResolved sets the artifact reference and media type for the
// handler.
OnReferenceResolved(reference string)

// ListSignature adds the signature digest to the tree.
ListSignature(signatureManifest ocispec.Descriptor)

// OnExceedMaxSignatures outputs the warning message when the number of
// signatures exceeds the maximum limit.
OnExceedMaxSignatures(err error)
}
66 changes: 66 additions & 0 deletions cmd/notation/internal/display/metadata/tree/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tree

import (
notationregistry "github.com/notaryproject/notation-go/registry"
"github.com/notaryproject/notation/cmd/notation/internal/display/output"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

// ListHandler is a handler for rendering signature metadata information in
// a tree format. It implements the ListHandler interface.
type ListHandler struct {
printer *output.Printer
root *node
signaturesNode *node
}

// NewListHandler creates a new ListHandler.
func NewListHandler(printer *output.Printer) *ListHandler {
return &ListHandler{
printer: printer,
}
}

// OnResolvingTagReference outputs the tag reference warning.
func (h *ListHandler) OnResolvingTagReference(reference string) {
h.printer.PrintErrorf("Warning: Always list the artifact using digest(@sha256:...) rather than a tag(:%s) because resolved digest may not point to the same signed artifact, as tags are mutable.\n", reference)
}

// OnReferenceResolved sets the artifact reference and media type for the
// handler.
func (h *ListHandler) OnReferenceResolved(reference string) {
h.root = newNode(reference)
h.signaturesNode = h.root.Add(notationregistry.ArtifactTypeNotation)
}

// ListSignature adds the signature digest to the tree.
func (h *ListHandler) ListSignature(signatureManifest ocispec.Descriptor) {
h.signaturesNode.Add(signatureManifest.Digest.String())
}

// OnExceedMaxSignatures outputs the warning message when the number of
// signatures exceeds the maximum limit.
func (h *ListHandler) OnExceedMaxSignatures(err error) {
h.printer.PrintErrorf("Warning: %v\n", err)
}

// Render prints the tree format of the signature metadata information.
func (h *ListHandler) Render() error {
if len(h.signaturesNode.Children) == 0 {
return h.printer.Printf("%s has no associated signatures\n", h.root.Value)
}
return h.root.Print(h.printer)
}
52 changes: 15 additions & 37 deletions cmd/notation/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ import (
"os"

notationregistry "github.com/notaryproject/notation-go/registry"
"github.com/notaryproject/notation/cmd/notation/internal/display"
cmderr "github.com/notaryproject/notation/cmd/notation/internal/errors"
"github.com/notaryproject/notation/cmd/notation/internal/experimental"
"github.com/notaryproject/notation/cmd/notation/internal/option"
"github.com/notaryproject/notation/internal/cmd"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
)

type listOpts struct {
cmd.LoggingFlagOpts
SecureFlagOpts
option.Common
reference string
allowReferrersAPI bool
ociLayout bool
Expand Down Expand Up @@ -75,6 +77,7 @@ Example - [Experimental] List signatures of an OCI artifact identified by a tag
if opts.ociLayout {
opts.inputType = inputTypeOCILayout
}
opts.Common.Parse(cmd)
return experimental.CheckFlagsAndWarn(cmd, "allow-referrers-api", "oci-layout")
},
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -101,60 +104,35 @@ func runList(ctx context.Context, opts *listOpts) error {
ctx = opts.LoggingFlagOpts.InitializeLogger(ctx)

// initialize
displayHandler := display.NewListHandler(opts.Printer)
reference := opts.reference
// always use the Referrers API, if not supported, automatically fallback to
// the referrers tag schema
sigRepo, err := getRepository(ctx, opts.inputType, reference, &opts.SecureFlagOpts, false)
if err != nil {
return err
}
targetDesc, resolvedRef, err := resolveReferenceWithWarning(ctx, opts.inputType, reference, sigRepo, "list")
manifestDesc, resolvedRef, err := resolveReference(ctx, opts.inputType, reference, sigRepo, func(ref string, manifestDesc ocispec.Descriptor) {
displayHandler.OnResolvingTagReference(ref)
})
if err != nil {
return err
}
// print all signature manifest digests
return printSignatureManifestDigests(ctx, targetDesc, sigRepo, resolvedRef, opts.maxSignatures)
}

// printSignatureManifestDigests returns the signature manifest digests of
// the subject manifest.
func printSignatureManifestDigests(ctx context.Context, targetDesc ocispec.Descriptor, sigRepo notationregistry.Repository, ref string, maxSigs int) error {
titlePrinted := false
printTitle := func() {
if !titlePrinted {
fmt.Println(ref)
fmt.Printf("└── %s\n", notationregistry.ArtifactTypeNotation)
titlePrinted = true
}
}
displayHandler.OnReferenceResolved(resolvedRef)

var prevDigest digest.Digest
err := listSignatures(ctx, sigRepo, targetDesc, maxSigs, func(sigManifestDesc ocispec.Descriptor) error {
// print the previous signature digest
if prevDigest != "" {
printTitle()
fmt.Printf(" ├── %s\n", prevDigest)
}
prevDigest = sigManifestDesc.Digest
// list signatures
if err := listSignatures(ctx, sigRepo, manifestDesc, opts.maxSignatures, func(sigManifestDesc ocispec.Descriptor) error {
displayHandler.ListSignature(sigManifestDesc)
return nil
})
// print the last signature digest
if prevDigest != "" {
printTitle()
fmt.Printf(" └── %s\n", prevDigest)
}
if err != nil {
}); err != nil {
var errExceedMaxSignatures cmderr.ErrorExceedMaxSignatures
if !errors.As(err, &errExceedMaxSignatures) {
return err
}
fmt.Println("Warning:", errExceedMaxSignatures)
displayHandler.OnExceedMaxSignatures(err)
}

if !titlePrinted {
fmt.Printf("%s has no associated signature\n", ref)
}
return nil
return displayHandler.Render()
}

// listSignatures lists signatures associated with manifestDesc with number of
Expand Down
36 changes: 31 additions & 5 deletions test/e2e/suite/command/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ var _ = Describe("notation list", func() {
}

notation.Exec("list", "--oci-layout", ociLayout.ReferenceWithDigest()).
MatchKeyWords(
"└── application/vnd.cncf.notary.signature",
"└── sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f",
)
MatchContent(ociLayout.ReferenceWithDigest() + `
└── application/vnd.cncf.notary.signature
└── sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f
`)
})
})

It("oci-layout with no signature", func() {
HostWithOCILayout(BaseOptionsWithExperimental(), func(notation *utils.ExecOpts, ociLayout *OCILayout, vhost *utils.VirtualHost) {
notation.Exec("list", "--oci-layout", ociLayout.ReferenceWithDigest()).
MatchKeyWords("has no associated signature")
MatchContent(ociLayout.ReferenceWithDigest() + " has no associated signatures\n")
})
})

Expand Down Expand Up @@ -157,4 +157,30 @@ var _ = Describe("notation list", func() {
)
})
})

It("show multiple signatures", func() {
Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) {
artifact := GenerateArtifact("e2e-valid-multiple-signatures", "")

notation.Exec("list", artifact.ReferenceWithDigest()).
MatchContent(artifact.ReferenceWithDigest() + `
└── application/vnd.cncf.notary.signature
├── sha256:c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf
└── sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f
`)
})
})

It("exceed max signatures", func() {
Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) {
artifact := GenerateArtifact("e2e-valid-multiple-signatures", "")

notation.Exec("list", "--max-signatures", "1", artifact.ReferenceWithDigest()).
MatchErrKeyWords("Warning: exceeded configured limit of max signatures 1 to examine").
MatchContent(artifact.ReferenceWithDigest() + `
└── application/vnd.cncf.notary.signature
└── sha256:c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf
`)
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"e38aea13f2b051f7183cca64a51ee676f4d4456f41d9fecf15eb367efc7343b9\"]","org.opencontainers.image.created":"2023-06-27T05:30:33Z"},"artifactType":"application/vnd.cncf.notary.signature"}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
awesome notation
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50","size":2070}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9","size":517},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"e38aea13f2b051f7183cca64a51ee676f4d4456f41d9fecf15eb367efc7343b9\"]","org.opencontainers.image.created":"2023-06-27T05:30:33Z"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6Yjg0NzlkZTNmODhmYjI1OWEwYTllYTgyYTViMmEwNTJhMWVmM2M0ZWJiY2ZjNjE0ODJkNWFlNGM4MzFmOGFmOSIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsInNpemUiOjUxN319","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAyLTE3VDA4OjE3OjA5WiJ9","header":{"x5c":["MIIDRjCCAi6gAwIBAgIBZzANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTETMBEGA1UEAxMKdGVzdGNlcnQxMTAeFw0yNTAyMTcwODE1NDlaFw0yNTAyMTgwODE1NDlaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEPMA0GA1UEChMGTm90YXJ5MRMwEQYDVQQDEwp0ZXN0Y2VydDExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4e5o57Z39his+TYHXdvpVw2RuW9a1VjbmWBfXtpG0ZQXV1EuTJ1C58n0zNlSoaXiwxiTCVo4bT8LA2AybsvNwR+MH95luFoVfXZuORcFffSsi76tM9xr3BbMAZE6rDTWahxEuurfkCoqrNLKV4Hl9uIWVPJCavEnUxiGSAUIKGcA+gbqSIRGrNrvDo6qqY8lNeXrbIWJemwzRLdRi+V71xq9tuphmEilzk+80uN7q4D4gdlAn2hpRFZmiSiaVCmZTaiW1miJBcus2r0eWwjCRSpgDg1GqpKKfw2Tncx5Xeg1Ki0Qk2M/GEhXp6zHWkkkHxP4pnx6AOVmMxa/gxZLjQIDAQABoycwJTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAJJ0H0mVpMCrpQcNHsp8AyWh/0yoou8esTCvCSKLX6bHjW6uHO6gCDiOQkOyUsnc//lMVBN4s3G3bbQMHoeMjZ0ptFjJN+Mq2PHAzeXZhC4jDnztDcpNXEyrKySSriG8AdJ4jxYbjCaHX1D/z+O47F6dNi2inXJgR+KYKt6SFWAGIKXEkjx4Etuc7t1nFOZYVlBSyY1KAahGUzfusoSTHNdF+ENTa9AgQOtH8x1P8vl74V8dTu7KuFTIYtnDHo8vmblWm0I+et6zSwAIbgQs7bAZXsMt3wvisD0mdn+QpKIEJYbTcPKQRkiZD4K5+6nmmP8rqLKekwWMSlP5zYhgmvc="],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"V2PFO3FSSSYTf4r7-1sYM_NkYbMN3-ALY-4goPGUn1LblUXsz5Lcgm23K3eQVe1rik949x7PO2TcvonPVWxvLwNiSTULUNxDXcH9oU0lhFPpF7_5_ZwrHxILTycVDs8xGR_aUC6uufQW0suJFwSxhsuRhfJIkmU6-H_pV1_DVP50Fff2haHzgppZBVUqQZmUGl16XaLOVIvYglVc2tyqFbN1S8NJTPMcZomiw4I52rYhSB18Ozyr43sGkBNV_gMJT7PK_0zwRKCdJ7d5Fy9eo3QkUiMiqyHkVsDrXPpqpAReo18P340BOkvQjtBYtXT-T2F6TbuIP6aLXfJ1G9VOGw"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.notation.config","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070","size":17,"annotations":{"org.opencontainers.image.title":"e2e.txt"}}],"annotations":{"org.opencontainers.image.created":"2023-06-27T05:01:36Z"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481","size":2093}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9","size":517},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"a10293ee855ac897fc6ffefadb0b72d4bd8042029f5108f404d7ca9b4324ca97\"]","org.opencontainers.image.created":"2025-02-17T08:17:09Z"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6Yjg0NzlkZTNmODhmYjI1OWEwYTllYTgyYTViMmEwNTJhMWVmM2M0ZWJiY2ZjNjE0ODJkNWFlNGM4MzFmOGFmOSIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsInNpemUiOjUxN319","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDIzLTA2LTI3VDEzOjMwOjMzKzA4OjAwIn0","header":{"x5c":["MIIDOjCCAiKgAwIBAgIBUTANBgkqhkiG9w0BAQsFADBLMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEMMAoGA1UEAxMDZTJlMCAXDTIyMTIxOTAyNDMzOVoYDzIxMjIxMjE5MDI0MzM5WjBLMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEMMAoGA1UEAxMDZTJlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwNrKIzpESDkY7Kah+sWKeXDNE7yUBaqW58cyMtkpk9/Q6QLbndMxdt8VjD3y4bSBhZXLdUkW0S4hSNZXQwkYm3yooxJXj+uOI9NnEPt/gAkPYri4TdKpTFSBzE4uJ0HrYDBUir3Dars/CXiusmdCnSWKsgm05ItkHdHEGtor716aWdnGuFNvyzJyaC3XLFpo1OwEwTyxf4Yix4UtvwNDB4TOJRH84avSoFCua8xbRpiBJ0QoX6zY0Yr9qbvMBQIcmpQ3uWOiEeVMDjRE0XzhiWSevVfZTPJcYpsAyBTJGJLIcHU7JMYnwKP6IzUg5T589lvRVD7up4sAC7izOKGzPQIDAQABoycwJTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAKacaJ+O9rTIdfLHCdneLQ6RN92Id5dl8PGh/Xig3AWSOtLgIiRRsdxVH5PZkGOHosellQG5fCvQOwB0x8O+2YDKLOfgVIJWd6X85NdvyCdX2ElYRmvX9ON5WVguGLluwkOfJ26M8d8ftXrcc97qaKk4EHS8R/LCWqZNDRiRCA0OtNP9cUkKFaIG1hgWgEieVWnxrCyUDbTX3uCiJKNzSOmI3psF3WIhabU7/7gBm95nWhgwS91qAxavccVkY6hqAPj9tjJH5UPI5RR8kDB5rWiCIx1YHuH+z1eAYUHWVvZvneVniNBI8qGoGBz9HkrX5ecf+V7zaxyy0FoWlEX1z8k="],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"vJnEBEYvmRaz9jpOKJzGGorX7COBww5KzXVLuZLeB0ponDApf15r4WhajjVXRlbh2qyxZP3v3PkR8-SVh7ku1SDQ416H1uNNjkKJdovnu-CNAdAborfJCUzTLRDyioUXgp8y098dyPabWNo_hNJautZVvQkerEWLul5c40SclrruKtrz1dK2w3BIcDQR6hOIlQ2VvwnmsCkzDbkljlWGwwjR9xfpePhzAMP4K9MQc0KR4Vmu3Qd9aBdxtI9NW477B2XC2jSJ6erh74HCDtJcIpVxI30i-2JXKvSlrATGGR_Tj5x8vFgpkHGmbIEJFLzjqM347O_Q0A5SLIW-rWE2WQ"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9","size":517,"annotations":{"org.opencontainers.image.ref.name":"v1"}},{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283","size":485,"annotations":{"org.opencontainers.image.ref.name":"sha256-b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f","size":728},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"a10293ee855ac897fc6ffefadb0b72d4bd8042029f5108f404d7ca9b4324ca97\"]","org.opencontainers.image.created":"2025-02-17T08:17:09Z"}}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"imageLayoutVersion":"1.0.0"}
Loading