From 5820dbf2505903943663e233db3066a01a44e9d7 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 17 Feb 2025 08:08:23 +0000 Subject: [PATCH 01/15] fix: complete codes Signed-off-by: Junjie Gao --- cmd/notation/internal/display/handler.go | 6 ++ .../internal/display/metadata/interface.go | 21 ++++++ .../internal/display/metadata/tree/list.go | 66 +++++++++++++++++++ cmd/notation/list.go | 52 +++++---------- test/e2e/suite/command/list.go | 10 +-- 5 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 cmd/notation/internal/display/metadata/tree/list.go diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index 4b2d6bdac..0a2dee5cc 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -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) +} diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 9099ff2fd..1959fff8e 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -76,3 +76,24 @@ 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) + + // OnSignatureResolved sets the signature manifest descriptor for + // the handler. + OnSignatureResolved(signatureManifest ocispec.Descriptor) + + // OnExceedMaxSignatures outputs the warning message when the number of + // signatures exceeds the maximum limit. + OnExceedMaxSignatures(err error) +} diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go new file mode 100644 index 000000000..9ad846e20 --- /dev/null +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -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) +} + +// OnSignatureResolved adds the signature digest to the list. +func (h *ListHandler) OnSignatureResolved(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) +} diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 387134907..aa63e9322 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -20,10 +20,11 @@ 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" ) @@ -31,6 +32,7 @@ import ( type listOpts struct { cmd.LoggingFlagOpts SecureFlagOpts + option.Common reference string allowReferrersAPI bool ociLayout bool @@ -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 { @@ -101,6 +104,7 @@ 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 @@ -108,53 +112,27 @@ func runList(ctx context.Context, opts *listOpts) error { 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.OnSignatureResolved(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 diff --git a/test/e2e/suite/command/list.go b/test/e2e/suite/command/list.go index 889ccb396..300901dcc 100644 --- a/test/e2e/suite/command/list.go +++ b/test/e2e/suite/command/list.go @@ -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") }) }) From 21799b36f31439330bce679ac397860beb06d5b6 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 17 Feb 2025 08:21:16 +0000 Subject: [PATCH 02/15] test: improve E2E Signed-off-by: Junjie Gao --- test/e2e/suite/command/list.go | 13 +++++++++++++ ...ff18998a3d39e226965c41477a1bc1765f7617632ff3f283 | 1 + ...1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a | 1 + ...4c98086f403d5fd8117776ebe5aad1609281403d13dea070 | 1 + ...97c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f | 1 + ...a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481 | 1 + ...a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 | 1 + ...8fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf | 1 + ...63e648986ad7273157511ffeba0b689f33e5472bae471f50 | 1 + .../e2e-valid-multiple-signatures/index.json | 1 + .../e2e-valid-multiple-signatures/oci-layout | 1 + 11 files changed, 23 insertions(+) create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/index.json create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/oci-layout diff --git a/test/e2e/suite/command/list.go b/test/e2e/suite/command/list.go index 300901dcc..cf23ed81c 100644 --- a/test/e2e/suite/command/list.go +++ b/test/e2e/suite/command/list.go @@ -157,4 +157,17 @@ 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 +`) + }) + }) }) diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283 new file mode 100644 index 000000000..46af4a72f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283 @@ -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"}]} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070 new file mode 100644 index 000000000..764af2de0 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070 @@ -0,0 +1 @@ +awesome notation diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f new file mode 100644 index 000000000..2ad47c0ca --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f @@ -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"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481 new file mode 100644 index 000000000..434f7fe25 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481 @@ -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"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 new file mode 100644 index 000000000..961e959cf --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 @@ -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"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf new file mode 100644 index 000000000..cee0e2416 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf @@ -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"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50 new file mode 100644 index 000000000..193127179 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50 @@ -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"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/index.json b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/index.json new file mode 100644 index 000000000..8cda1e0af --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/index.json @@ -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"}}]} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/oci-layout b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/oci-layout new file mode 100644 index 000000000..1343d370f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/oci-layout @@ -0,0 +1 @@ +{"imageLayoutVersion":"1.0.0"} \ No newline at end of file From bc35b6636eb3d1c323831edc5ae0de5fa58828c9 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 17 Feb 2025 08:24:50 +0000 Subject: [PATCH 03/15] fix: update interface Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/interface.go | 5 ++--- cmd/notation/internal/display/metadata/tree/list.go | 4 ++-- cmd/notation/list.go | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 1959fff8e..e9e4e5dab 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -89,9 +89,8 @@ type ListHandler interface { // handler. OnReferenceResolved(reference string) - // OnSignatureResolved sets the signature manifest descriptor for - // the handler. - OnSignatureResolved(signatureManifest ocispec.Descriptor) + // 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. diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index 9ad846e20..c637d895f 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -46,8 +46,8 @@ func (h *ListHandler) OnReferenceResolved(reference string) { h.signaturesNode = h.root.Add(notationregistry.ArtifactTypeNotation) } -// OnSignatureResolved adds the signature digest to the list. -func (h *ListHandler) OnSignatureResolved(signatureManifest ocispec.Descriptor) { +// ListSignature adds the signature digest to the tree. +func (h *ListHandler) ListSignature(signatureManifest ocispec.Descriptor) { h.signaturesNode.Add(signatureManifest.Digest.String()) } diff --git a/cmd/notation/list.go b/cmd/notation/list.go index aa63e9322..9dea26c27 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -122,7 +122,7 @@ func runList(ctx context.Context, opts *listOpts) error { // list signatures if err := listSignatures(ctx, sigRepo, manifestDesc, opts.maxSignatures, func(sigManifestDesc ocispec.Descriptor) error { - displayHandler.OnSignatureResolved(sigManifestDesc) + displayHandler.ListSignature(sigManifestDesc) return nil }); err != nil { var errExceedMaxSignatures cmderr.ErrorExceedMaxSignatures From cde3cf18e75a245101e3d8a3cfcbaee341201d7e Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 17 Feb 2025 08:26:46 +0000 Subject: [PATCH 04/15] fix: improve code Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/tree/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index c637d895f..7f1174f9a 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -59,7 +59,7 @@ func (h *ListHandler) OnExceedMaxSignatures(err error) { // Render prints the tree format of the signature metadata information. func (h *ListHandler) Render() error { - if len(h.signaturesNode.Children) == 0 { + if h.root == nil || h.signaturesNode == nil || len(h.signaturesNode.Children) == 0 { return h.printer.Printf("%s has no associated signatures\n", h.root.Value) } return h.root.Print(h.printer) From e7bf3aade9e5ba329721b657126c9dd1f5a47061 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 17 Feb 2025 08:29:00 +0000 Subject: [PATCH 05/15] fix: update Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/tree/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index 7f1174f9a..c637d895f 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -59,7 +59,7 @@ func (h *ListHandler) OnExceedMaxSignatures(err error) { // Render prints the tree format of the signature metadata information. func (h *ListHandler) Render() error { - if h.root == nil || h.signaturesNode == nil || len(h.signaturesNode.Children) == 0 { + 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) From 3b32b0d445f01b647b480150b9f3a2a36f269a48 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 17 Feb 2025 08:36:10 +0000 Subject: [PATCH 06/15] fix: improve E2E Signed-off-by: Junjie Gao --- test/e2e/run.sh | 2 +- test/e2e/suite/command/list.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/test/e2e/run.sh b/test/e2e/run.sh index 239d6a219..2c7f63e0e 100755 --- a/test/e2e/run.sh +++ b/test/e2e/run.sh @@ -127,4 +127,4 @@ export NOTATION_E2E_BLOB_TRUST_POLICY_PATH=$CWD/testdata/blob/trustpolicies export NOTATION_E2E_TEST_DATA_PATH=$CWD/testdata # run tests -ginkgo -r -p -v +ginkgo -r -p -v --focus "exceed max signatures" diff --git a/test/e2e/suite/command/list.go b/test/e2e/suite/command/list.go index cf23ed81c..36c781f3e 100644 --- a/test/e2e/suite/command/list.go +++ b/test/e2e/suite/command/list.go @@ -167,6 +167,19 @@ var _ = Describe("notation list", func() { └── 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 `) }) }) From 1772aa0e7a6567a4e06e00c814a1f3c03c0668a3 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 17 Feb 2025 08:36:34 +0000 Subject: [PATCH 07/15] fix: complete code Signed-off-by: Junjie Gao --- test/e2e/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/run.sh b/test/e2e/run.sh index 2c7f63e0e..239d6a219 100755 --- a/test/e2e/run.sh +++ b/test/e2e/run.sh @@ -127,4 +127,4 @@ export NOTATION_E2E_BLOB_TRUST_POLICY_PATH=$CWD/testdata/blob/trustpolicies export NOTATION_E2E_TEST_DATA_PATH=$CWD/testdata # run tests -ginkgo -r -p -v --focus "exceed max signatures" +ginkgo -r -p -v From b412fd9e636dc81c80d21f557dcb2b350700e946 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 18 Feb 2025 04:18:55 +0000 Subject: [PATCH 08/15] fix: add streaming printer Signed-off-by: Junjie Gao --- .../internal/display/metadata/interface.go | 4 +- .../internal/display/metadata/tree/inspect.go | 5 +- .../internal/display/metadata/tree/list.go | 43 +++++--- .../internal/display/metadata/tree/printer.go | 52 ++++++++++ .../display/metadata/tree/printer_test.go | 99 +++++++++++++++++++ cmd/notation/list.go | 2 +- 6 files changed, 189 insertions(+), 16 deletions(-) create mode 100644 cmd/notation/internal/display/metadata/tree/printer.go create mode 100644 cmd/notation/internal/display/metadata/tree/printer_test.go diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index e9e4e5dab..24a56ca84 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -89,8 +89,8 @@ type ListHandler interface { // handler. OnReferenceResolved(reference string) - // ListSignature adds the signature digest to the tree. - ListSignature(signatureManifest ocispec.Descriptor) + // OnSignatureListed adds the signature digest to the tree. + OnSignatureListed(signatureManifest ocispec.Descriptor) // OnExceedMaxSignatures outputs the warning message when the number of // signatures exceeds the maximum limit. diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index aaf94cb5a..9d5da3a9a 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -25,6 +25,8 @@ import ( type InspectHandler struct { printer *output.Printer + sprinter *streamingPrinter + // rootReferenceNode is the root node with the artifact reference as the // value. rootReferenceNode *node @@ -37,7 +39,8 @@ type InspectHandler struct { // tree format. func NewInspectHandler(printer *output.Printer) *InspectHandler { return &InspectHandler{ - printer: printer, + printer: printer, + sprinter: newStreamingPrinter(" ", printer), } } diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index c637d895f..7579c52f7 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -22,15 +22,28 @@ import ( // 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 + printer *output.Printer + + // sprinter is a streaming printer to print the signature digest nodes in + // a streaming fashion + sprinter *streamingPrinter + + // headerNode contains the headers of the output + // + // example: + // localhost:5000/net-monitor@sha256:b94d27b9934d3e08a52e52d7da7dabfac4efe37a5380ee9088f7ace2efcde9 + // └── application/vnd.cncf.notary.signature + headerNode *node + + // headerPrinted is a flag to indicate if the header has been printed + headerPrinted bool } // NewListHandler creates a new ListHandler. func NewListHandler(printer *output.Printer) *ListHandler { return &ListHandler{ - printer: printer, + printer: printer, + sprinter: newStreamingPrinter(" ", printer), } } @@ -42,13 +55,18 @@ func (h *ListHandler) OnResolvingTagReference(reference string) { // 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) + h.headerNode = newNode(reference) + h.headerNode.Add(notationregistry.ArtifactTypeNotation) } -// ListSignature adds the signature digest to the tree. -func (h *ListHandler) ListSignature(signatureManifest ocispec.Descriptor) { - h.signaturesNode.Add(signatureManifest.Digest.String()) +// OnSignatureListed adds the signature digest to the tree. +func (h *ListHandler) OnSignatureListed(signatureManifest ocispec.Descriptor) { + // print the header + if !h.headerPrinted { + h.headerNode.Print(h.printer) + h.headerPrinted = true + } + h.sprinter.PrintNode(newNode(signatureManifest.Digest.String())) } // OnExceedMaxSignatures outputs the warning message when the number of @@ -59,8 +77,9 @@ func (h *ListHandler) OnExceedMaxSignatures(err error) { // 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) + if h.sprinter.prevNode == nil { + return h.printer.Printf("%s has no associated signatures\n", h.headerNode.Value) } - return h.root.Print(h.printer) + h.sprinter.Complete() + return nil } diff --git a/cmd/notation/internal/display/metadata/tree/printer.go b/cmd/notation/internal/display/metadata/tree/printer.go new file mode 100644 index 000000000..7b0d34da2 --- /dev/null +++ b/cmd/notation/internal/display/metadata/tree/printer.go @@ -0,0 +1,52 @@ +// 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 "io" + +// streamingPrinter prints the tree nodes in a streaming fashion. +type streamingPrinter struct { + w io.Writer + prefix string + prevNode *node +} + +// newStreamingPrinter creates a new streaming printer. +// +// prefix is the prefix string that will be inherited by the nodes. +func newStreamingPrinter(prefix string, w io.Writer) *streamingPrinter { + return &streamingPrinter{ + w: w, + prefix: prefix, + } +} + +// PrintNode adds a new node to be ready to print. +func (p *streamingPrinter) PrintNode(node *node) { + if p.prevNode == nil { + p.prevNode = node + return + } + print(p.w, p.prefix, treeItemPrefix, p.prefix+subTreePrefix, p.prevNode) + p.prevNode = node +} + +// Complete prints the last node and completes the printing. +func (p *streamingPrinter) Complete() { + if p.prevNode != nil { + // print the last node + print(p.w, p.prefix, treeItemPrefixLast, p.prefix+subTreePrefixLast, p.prevNode) + p.prevNode = nil + } +} diff --git a/cmd/notation/internal/display/metadata/tree/printer_test.go b/cmd/notation/internal/display/metadata/tree/printer_test.go new file mode 100644 index 000000000..a54cd3cc4 --- /dev/null +++ b/cmd/notation/internal/display/metadata/tree/printer_test.go @@ -0,0 +1,99 @@ +package tree + +import ( + "bytes" + "testing" +) + +func TestStreamingPrinter(t *testing.T) { + t.Run("one node", func(t *testing.T) { + expected := "└── a\n" + buff := &bytes.Buffer{} + p := newStreamingPrinter("", buff) + p.PrintNode(newNode("a")) + p.Complete() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) + + t.Run("two nodes", func(t *testing.T) { + expected := `├── a +└── b +` + buff := &bytes.Buffer{} + p := newStreamingPrinter("", buff) + p.PrintNode(newNode("a")) + p.PrintNode(newNode("b")) + p.Complete() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) + + t.Run("two node with complex structure", func(t *testing.T) { + expected := `├── a +│ ├── b +│ │ └── c +│ └── d +└── e + ├── f + │ └── g + └── h +` + buff := &bytes.Buffer{} + p := newStreamingPrinter("", buff) + // create the tree + a := newNode("a") + b := a.Add("b") + b.Add("c") + a.Add("d") + p.PrintNode(a) + + e := newNode("e") + f := e.Add("f") + f.Add("g") + e.Add("h") + p.PrintNode(e) + + p.Complete() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) + + t.Run("two node with prefix", func(t *testing.T) { + expected := ` │ ├── a + │ │ ├── b + │ │ │ └── c + │ │ └── d + │ └── e + │ ├── f + │ │ └── g + │ └── h +` + buff := &bytes.Buffer{} + p := newStreamingPrinter(" │ ", buff) + // create the tree + a := newNode("a") + b := a.Add("b") + b.Add("c") + a.Add("d") + p.PrintNode(a) + + e := newNode("e") + f := e.Add("f") + f.Add("g") + e.Add("h") + p.PrintNode(e) + + p.Complete() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) +} diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 9dea26c27..3ee6a3b2d 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -122,7 +122,7 @@ func runList(ctx context.Context, opts *listOpts) error { // list signatures if err := listSignatures(ctx, sigRepo, manifestDesc, opts.maxSignatures, func(sigManifestDesc ocispec.Descriptor) error { - displayHandler.ListSignature(sigManifestDesc) + displayHandler.OnSignatureListed(sigManifestDesc) return nil }); err != nil { var errExceedMaxSignatures cmderr.ErrorExceedMaxSignatures From de72925abd3d0982fbec06dcbfffb9cb4e682765 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 18 Feb 2025 04:34:22 +0000 Subject: [PATCH 09/15] fix: update Signed-off-by: Junjie Gao --- .../internal/display/metadata/interface.go | 2 +- .../internal/display/metadata/tree/inspect.go | 5 +--- .../internal/display/metadata/tree/printer.go | 3 ++- .../display/metadata/tree/printer_test.go | 24 +++++++++++++++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 24a56ca84..9f1f0b93a 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -89,7 +89,7 @@ type ListHandler interface { // handler. OnReferenceResolved(reference string) - // OnSignatureListed adds the signature digest to the tree. + // OnSignatureListed adds the signature digest to be rendered. OnSignatureListed(signatureManifest ocispec.Descriptor) // OnExceedMaxSignatures outputs the warning message when the number of diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 9d5da3a9a..aaf94cb5a 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -25,8 +25,6 @@ import ( type InspectHandler struct { printer *output.Printer - sprinter *streamingPrinter - // rootReferenceNode is the root node with the artifact reference as the // value. rootReferenceNode *node @@ -39,8 +37,7 @@ type InspectHandler struct { // tree format. func NewInspectHandler(printer *output.Printer) *InspectHandler { return &InspectHandler{ - printer: printer, - sprinter: newStreamingPrinter(" ", printer), + printer: printer, } } diff --git a/cmd/notation/internal/display/metadata/tree/printer.go b/cmd/notation/internal/display/metadata/tree/printer.go index 7b0d34da2..1c050b6e5 100644 --- a/cmd/notation/internal/display/metadata/tree/printer.go +++ b/cmd/notation/internal/display/metadata/tree/printer.go @@ -24,7 +24,8 @@ type streamingPrinter struct { // newStreamingPrinter creates a new streaming printer. // -// prefix is the prefix string that will be inherited by the nodes. +// prefix is the prefix string that will be inherited by the nodes that are +// printed. func newStreamingPrinter(prefix string, w io.Writer) *streamingPrinter { return &streamingPrinter{ w: w, diff --git a/cmd/notation/internal/display/metadata/tree/printer_test.go b/cmd/notation/internal/display/metadata/tree/printer_test.go index a54cd3cc4..1a59db047 100644 --- a/cmd/notation/internal/display/metadata/tree/printer_test.go +++ b/cmd/notation/internal/display/metadata/tree/printer_test.go @@ -1,3 +1,16 @@ +// 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 ( @@ -6,6 +19,17 @@ import ( ) func TestStreamingPrinter(t *testing.T) { + t.Run("empty output", func(t *testing.T) { + expected := "" + buff := &bytes.Buffer{} + p := newStreamingPrinter("", buff) + p.Complete() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) + t.Run("one node", func(t *testing.T) { expected := "└── a\n" buff := &bytes.Buffer{} From 2abca215e511ad69b857b5b0b6fe6b60fb6e65b3 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 18 Feb 2025 05:17:34 +0000 Subject: [PATCH 10/15] fix: update Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/tree/list.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index 7579c52f7..2062df958 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -19,8 +19,8 @@ import ( 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. +// ListHandler is a handler for rendering a list of signature digests in +// streaming fashion. It implements the metadata.ListHandler interface. type ListHandler struct { printer *output.Printer @@ -59,7 +59,7 @@ func (h *ListHandler) OnReferenceResolved(reference string) { h.headerNode.Add(notationregistry.ArtifactTypeNotation) } -// OnSignatureListed adds the signature digest to the tree. +// OnSignatureListed adds the signature digest to be printed. func (h *ListHandler) OnSignatureListed(signatureManifest ocispec.Descriptor) { // print the header if !h.headerPrinted { @@ -75,7 +75,7 @@ func (h *ListHandler) OnExceedMaxSignatures(err error) { h.printer.PrintErrorf("Warning: %v\n", err) } -// Render prints the tree format of the signature metadata information. +// Render completes the rendering of the list of signature digests. func (h *ListHandler) Render() error { if h.sprinter.prevNode == nil { return h.printer.Printf("%s has no associated signatures\n", h.headerNode.Value) From 83d602b69caf20919323ae0087355413324ff752 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 21 Feb 2025 03:41:48 +0000 Subject: [PATCH 11/15] fix: update Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/interface.go | 8 -------- cmd/notation/internal/display/metadata/tree/list.go | 11 ----------- cmd/notation/list.go | 4 ++-- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 9f1f0b93a..59e525220 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -81,18 +81,10 @@ type BlobVerifyHandler interface { // 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) // OnSignatureListed adds the signature digest to be rendered. OnSignatureListed(signatureManifest ocispec.Descriptor) - - // OnExceedMaxSignatures outputs the warning message when the number of - // signatures exceeds the maximum limit. - OnExceedMaxSignatures(err error) } diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index 2062df958..b0f9054d3 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -47,11 +47,6 @@ func NewListHandler(printer *output.Printer) *ListHandler { } } -// 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) { @@ -69,12 +64,6 @@ func (h *ListHandler) OnSignatureListed(signatureManifest ocispec.Descriptor) { h.sprinter.PrintNode(newNode(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 completes the rendering of the list of signature digests. func (h *ListHandler) Render() error { if h.sprinter.prevNode == nil { diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 3ee6a3b2d..5a573226a 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -113,7 +113,7 @@ func runList(ctx context.Context, opts *listOpts) error { return err } manifestDesc, resolvedRef, err := resolveReference(ctx, opts.inputType, reference, sigRepo, func(ref string, manifestDesc ocispec.Descriptor) { - displayHandler.OnResolvingTagReference(ref) + opts.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", ref) }) if err != nil { return err @@ -129,7 +129,7 @@ func runList(ctx context.Context, opts *listOpts) error { if !errors.As(err, &errExceedMaxSignatures) { return err } - displayHandler.OnExceedMaxSignatures(err) + opts.Printer.PrintErrorf("Warning: %v\n", err) } return displayHandler.Render() From bbc19bb0e18aa5a41842f1a3e0bdc0877e0c01da Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 24 Feb 2025 02:47:34 +0000 Subject: [PATCH 12/15] fix: resolve comments Signed-off-by: Junjie Gao --- .../internal/display/metadata/interface.go | 3 ++- .../internal/display/metadata/tree/list.go | 18 ++++++++----- .../internal/display/metadata/tree/printer.go | 27 ++++++++++--------- .../display/metadata/tree/printer_test.go | 20 +++++++------- cmd/notation/list.go | 5 +--- test/e2e/suite/command/list.go | 14 ++++++---- 6 files changed, 48 insertions(+), 39 deletions(-) diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 59e525220..62f6f1716 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -81,10 +81,11 @@ type BlobVerifyHandler interface { // signatures. type ListHandler interface { Renderer + // OnReferenceResolved sets the artifact reference and media type for the // handler. OnReferenceResolved(reference string) // OnSignatureListed adds the signature digest to be rendered. - OnSignatureListed(signatureManifest ocispec.Descriptor) + OnSignatureListed(signatureManifest ocispec.Descriptor) error } diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index b0f9054d3..af49c7007 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -26,7 +26,7 @@ type ListHandler struct { // sprinter is a streaming printer to print the signature digest nodes in // a streaming fashion - sprinter *streamingPrinter + sprinter *StreamPrinter // headerNode contains the headers of the output // @@ -43,7 +43,7 @@ type ListHandler struct { func NewListHandler(printer *output.Printer) *ListHandler { return &ListHandler{ printer: printer, - sprinter: newStreamingPrinter(" ", printer), + sprinter: newStreamPrinter(" ", printer), } } @@ -55,20 +55,24 @@ func (h *ListHandler) OnReferenceResolved(reference string) { } // OnSignatureListed adds the signature digest to be printed. -func (h *ListHandler) OnSignatureListed(signatureManifest ocispec.Descriptor) { +func (h *ListHandler) OnSignatureListed(signatureManifest ocispec.Descriptor) error { // print the header if !h.headerPrinted { - h.headerNode.Print(h.printer) + if err := h.headerNode.Print(h.printer); err != nil { + return err + } h.headerPrinted = true } - h.sprinter.PrintNode(newNode(signatureManifest.Digest.String())) + return h.sprinter.PrintNode(newNode(signatureManifest.Digest.String())) } // Render completes the rendering of the list of signature digests. func (h *ListHandler) Render() error { - if h.sprinter.prevNode == nil { + if err := h.sprinter.Flush(); err != nil { + return err + } + if !h.headerPrinted { return h.printer.Printf("%s has no associated signatures\n", h.headerNode.Value) } - h.sprinter.Complete() return nil } diff --git a/cmd/notation/internal/display/metadata/tree/printer.go b/cmd/notation/internal/display/metadata/tree/printer.go index 1c050b6e5..82bad806e 100644 --- a/cmd/notation/internal/display/metadata/tree/printer.go +++ b/cmd/notation/internal/display/metadata/tree/printer.go @@ -15,39 +15,42 @@ package tree import "io" -// streamingPrinter prints the tree nodes in a streaming fashion. -type streamingPrinter struct { +// StreamPrinter prints the tree nodes in a streaming fashion. +type StreamPrinter struct { w io.Writer prefix string prevNode *node } -// newStreamingPrinter creates a new streaming printer. +// newStreamPrinter creates a new stream printer. // // prefix is the prefix string that will be inherited by the nodes that are // printed. -func newStreamingPrinter(prefix string, w io.Writer) *streamingPrinter { - return &streamingPrinter{ +func newStreamPrinter(prefix string, w io.Writer) *StreamPrinter { + return &StreamPrinter{ w: w, prefix: prefix, } } // PrintNode adds a new node to be ready to print. -func (p *streamingPrinter) PrintNode(node *node) { +func (p *StreamPrinter) PrintNode(node *node) error { if p.prevNode == nil { p.prevNode = node - return + return nil + } + if err := print(p.w, p.prefix, treeItemPrefix, p.prefix+subTreePrefix, p.prevNode); err != nil { + return err } - print(p.w, p.prefix, treeItemPrefix, p.prefix+subTreePrefix, p.prevNode) p.prevNode = node + return nil } -// Complete prints the last node and completes the printing. -func (p *streamingPrinter) Complete() { +// Flush prints the last node and completes the printing. +func (p *StreamPrinter) Flush() error { if p.prevNode != nil { // print the last node - print(p.w, p.prefix, treeItemPrefixLast, p.prefix+subTreePrefixLast, p.prevNode) - p.prevNode = nil + return print(p.w, p.prefix, treeItemPrefixLast, p.prefix+subTreePrefixLast, p.prevNode) } + return nil } diff --git a/cmd/notation/internal/display/metadata/tree/printer_test.go b/cmd/notation/internal/display/metadata/tree/printer_test.go index 1a59db047..0b78a023f 100644 --- a/cmd/notation/internal/display/metadata/tree/printer_test.go +++ b/cmd/notation/internal/display/metadata/tree/printer_test.go @@ -22,8 +22,8 @@ func TestStreamingPrinter(t *testing.T) { t.Run("empty output", func(t *testing.T) { expected := "" buff := &bytes.Buffer{} - p := newStreamingPrinter("", buff) - p.Complete() + p := newStreamPrinter("", buff) + p.Flush() if buff.String() != expected { t.Fatalf("expected %s, got %s", expected, buff.String()) @@ -33,9 +33,9 @@ func TestStreamingPrinter(t *testing.T) { t.Run("one node", func(t *testing.T) { expected := "└── a\n" buff := &bytes.Buffer{} - p := newStreamingPrinter("", buff) + p := newStreamPrinter("", buff) p.PrintNode(newNode("a")) - p.Complete() + p.Flush() if buff.String() != expected { t.Fatalf("expected %s, got %s", expected, buff.String()) @@ -47,10 +47,10 @@ func TestStreamingPrinter(t *testing.T) { └── b ` buff := &bytes.Buffer{} - p := newStreamingPrinter("", buff) + p := newStreamPrinter("", buff) p.PrintNode(newNode("a")) p.PrintNode(newNode("b")) - p.Complete() + p.Flush() if buff.String() != expected { t.Fatalf("expected %s, got %s", expected, buff.String()) @@ -68,7 +68,7 @@ func TestStreamingPrinter(t *testing.T) { └── h ` buff := &bytes.Buffer{} - p := newStreamingPrinter("", buff) + p := newStreamPrinter("", buff) // create the tree a := newNode("a") b := a.Add("b") @@ -82,7 +82,7 @@ func TestStreamingPrinter(t *testing.T) { e.Add("h") p.PrintNode(e) - p.Complete() + p.Flush() if buff.String() != expected { t.Fatalf("expected %s, got %s", expected, buff.String()) @@ -100,7 +100,7 @@ func TestStreamingPrinter(t *testing.T) { │ └── h ` buff := &bytes.Buffer{} - p := newStreamingPrinter(" │ ", buff) + p := newStreamPrinter(" │ ", buff) // create the tree a := newNode("a") b := a.Add("b") @@ -114,7 +114,7 @@ func TestStreamingPrinter(t *testing.T) { e.Add("h") p.PrintNode(e) - p.Complete() + p.Flush() if buff.String() != expected { t.Fatalf("expected %s, got %s", expected, buff.String()) diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 5a573226a..621c942a2 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -121,10 +121,7 @@ func runList(ctx context.Context, opts *listOpts) error { displayHandler.OnReferenceResolved(resolvedRef) // list signatures - if err := listSignatures(ctx, sigRepo, manifestDesc, opts.maxSignatures, func(sigManifestDesc ocispec.Descriptor) error { - displayHandler.OnSignatureListed(sigManifestDesc) - return nil - }); err != nil { + if err := listSignatures(ctx, sigRepo, manifestDesc, opts.maxSignatures, displayHandler.OnSignatureListed); err != nil { var errExceedMaxSignatures cmderr.ErrorExceedMaxSignatures if !errors.As(err, &errExceedMaxSignatures) { return err diff --git a/test/e2e/suite/command/list.go b/test/e2e/suite/command/list.go index 36c781f3e..e0d4fe192 100644 --- a/test/e2e/suite/command/list.go +++ b/test/e2e/suite/command/list.go @@ -163,11 +163,15 @@ var _ = Describe("notation list", func() { artifact := GenerateArtifact("e2e-valid-multiple-signatures", "") notation.Exec("list", artifact.ReferenceWithDigest()). - MatchContent(artifact.ReferenceWithDigest() + ` -└── application/vnd.cncf.notary.signature - ├── sha256:c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf - └── sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f -`) + MatchKeyWords( + artifact.ReferenceWithDigest(), + // the order of the signatures is not guaranteed + "└── application/vnd.cncf.notary.signature", + " ├── ", + " └── ", + "sha256:c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf", + "sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f", + ) }) }) From 3d75dc3d1b1b15a37d1cca9ea4557c8819ed54ba Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 24 Feb 2025 02:58:19 +0000 Subject: [PATCH 13/15] fix: update Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/tree/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index af49c7007..7b78aeaef 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -24,7 +24,7 @@ import ( type ListHandler struct { printer *output.Printer - // sprinter is a streaming printer to print the signature digest nodes in + // sprinter is a stream printer to print the signature digest nodes in // a streaming fashion sprinter *StreamPrinter From 68550ac7d7f1e1bbc59ea2779f3b9e3540c7d72a Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 24 Feb 2025 04:08:58 +0000 Subject: [PATCH 14/15] fix: update Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/tree/list.go | 2 +- .../internal/display/metadata/tree/printer.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index 7b78aeaef..c524762ab 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -26,7 +26,7 @@ type ListHandler struct { // sprinter is a stream printer to print the signature digest nodes in // a streaming fashion - sprinter *StreamPrinter + sprinter *streamPrinter // headerNode contains the headers of the output // diff --git a/cmd/notation/internal/display/metadata/tree/printer.go b/cmd/notation/internal/display/metadata/tree/printer.go index 82bad806e..d4c8be45a 100644 --- a/cmd/notation/internal/display/metadata/tree/printer.go +++ b/cmd/notation/internal/display/metadata/tree/printer.go @@ -15,8 +15,8 @@ package tree import "io" -// StreamPrinter prints the tree nodes in a streaming fashion. -type StreamPrinter struct { +// streamPrinter prints the tree nodes in a streaming fashion. +type streamPrinter struct { w io.Writer prefix string prevNode *node @@ -26,15 +26,15 @@ type StreamPrinter struct { // // prefix is the prefix string that will be inherited by the nodes that are // printed. -func newStreamPrinter(prefix string, w io.Writer) *StreamPrinter { - return &StreamPrinter{ +func newStreamPrinter(prefix string, w io.Writer) *streamPrinter { + return &streamPrinter{ w: w, prefix: prefix, } } // PrintNode adds a new node to be ready to print. -func (p *StreamPrinter) PrintNode(node *node) error { +func (p *streamPrinter) PrintNode(node *node) error { if p.prevNode == nil { p.prevNode = node return nil @@ -47,7 +47,7 @@ func (p *StreamPrinter) PrintNode(node *node) error { } // Flush prints the last node and completes the printing. -func (p *StreamPrinter) Flush() error { +func (p *streamPrinter) Flush() error { if p.prevNode != nil { // print the last node return print(p.w, p.prefix, treeItemPrefixLast, p.prefix+subTreePrefixLast, p.prevNode) From 3791fbfa2ea7680606baea33c50740f7c920c62c Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 24 Feb 2025 04:40:11 +0000 Subject: [PATCH 15/15] fix: update Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/tree/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go index c524762ab..871d0ca48 100644 --- a/cmd/notation/internal/display/metadata/tree/list.go +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -43,7 +43,7 @@ type ListHandler struct { func NewListHandler(printer *output.Printer) *ListHandler { return &ListHandler{ printer: printer, - sprinter: newStreamPrinter(" ", printer), + sprinter: newStreamPrinter(subTreePrefixLast, printer), } }