From 00189032643c768d288886b46e29c0b993a842bc Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Tue, 12 Mar 2024 17:35:56 -0600 Subject: [PATCH] support repo option in download sub cmd Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/download/download.go | 15 ++++++++++-- pkg/cmd/attestation/download/download_test.go | 23 ++++++++++++++++++- pkg/cmd/attestation/download/options.go | 1 + .../attestation/verification/attestation.go | 4 ++-- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/attestation/download/download.go b/pkg/cmd/attestation/download/download.go index 1c2aa6f10..ec2d30e28 100644 --- a/pkg/cmd/attestation/download/download.go +++ b/pkg/cmd/attestation/download/download.go @@ -10,6 +10,7 @@ import ( "github.com/cli/cli/v2/pkg/cmd/attestation/artifact/oci" "github.com/cli/cli/v2/pkg/cmd/attestation/auth" "github.com/cli/cli/v2/pkg/cmd/attestation/logging" + "github.com/cli/cli/v2/pkg/cmd/attestation/verification" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/MakeNowJust/heredoc" @@ -97,7 +98,9 @@ func NewDownloadCmd(f *cmdutil.Factory) *cobra.Command { } downloadCmd.Flags().StringVarP(&opts.Owner, "owner", "o", "", "a GitHub organization to scope attestation lookup by") - downloadCmd.MarkFlagRequired("owner") //nolint:errcheck + downloadCmd.Flags().StringVarP(&opts.Repo, "repo", "R", "", "Repository name in the format /") + downloadCmd.MarkFlagsMutuallyExclusive("owner", "repo") + downloadCmd.MarkFlagsOneRequired("owner", "repo") cmdutil.StringEnumFlag(downloadCmd, &opts.DigestAlgorithm, "digest-alg", "d", "sha256", []string{"sha256", "sha512"}, "The algorithm used to compute a digest of the artifact") downloadCmd.Flags().IntVarP(&opts.Limit, "limit", "L", api.DefaultLimit, "Maximum number of attestations to fetch") downloadCmd.Flags().BoolVarP(&opts.Verbose, "verbose", "v", false, "If set to true, the CLI will output verbose information.") @@ -115,7 +118,15 @@ func RunDownload(opts *Options) error { } opts.Logger.VerbosePrintf("Downloading trusted metadata for artifact %s\n\n", opts.ArtifactPath) - attestations, err := opts.APIClient.GetByOwnerAndDigest(opts.Owner, artifact.DigestWithAlg(), opts.Limit) + + c := verification.FetchAttestationsConfig{ + APIClient: opts.APIClient, + Digest: artifact.DigestWithAlg(), + Limit: opts.Limit, + Owner: opts.Owner, + Repo: opts.Repo, + } + attestations, err := verification.GetRemoteAttestations(c) if err != nil { return fmt.Errorf("failed to fetch attestations: %w", err) } diff --git a/pkg/cmd/attestation/download/download_test.go b/pkg/cmd/attestation/download/download_test.go index 817a9395c..f2c7fccee 100644 --- a/pkg/cmd/attestation/download/download_test.go +++ b/pkg/cmd/attestation/download/download_test.go @@ -23,12 +23,13 @@ func TestRunDownload(t *testing.T) { APIClient: api.NewTestClient(), OCIClient: oci.MockClient{}, DigestAlgorithm: "sha512", + Owner: "sigstore", OutputPath: tempDir, Limit: 30, Logger: logging.NewTestLogger(), } - t.Run("fetch and store attestations successfully", func(t *testing.T) { + t.Run("fetch and store attestations successfully with owner", func(t *testing.T) { err := RunDownload(&baseOpts) require.NoError(t, err) @@ -44,6 +45,26 @@ func TestRunDownload(t *testing.T) { require.Equal(t, expectedLineCount, actualLineCount) }) + t.Run("fetch and store attestations successfully with repo", func(t *testing.T) { + opts := baseOpts + opts.Owner = "" + opts.Repo = "sigstore/sigstore-js" + + err := RunDownload(&opts) + require.NoError(t, err) + + artifact, err := artifact.NewDigestedArtifact(opts.OCIClient, opts.ArtifactPath, opts.DigestAlgorithm) + require.NoError(t, err) + + require.FileExists(t, fmt.Sprintf("%s/%s.jsonl", tempDir, artifact.DigestWithAlg())) + + actualLineCount, err := countLines(fmt.Sprintf("%s/%s.jsonl", tempDir, artifact.DigestWithAlg())) + require.NoError(t, err) + + expectedLineCount := 2 + require.Equal(t, expectedLineCount, actualLineCount) + }) + t.Run("download OCI image attestations successfully", func(t *testing.T) { opts := baseOpts opts.ArtifactPath = "oci://ghcr.io/github/test" diff --git a/pkg/cmd/attestation/download/options.go b/pkg/cmd/attestation/download/options.go index 354140820..0c353c039 100644 --- a/pkg/cmd/attestation/download/options.go +++ b/pkg/cmd/attestation/download/options.go @@ -18,6 +18,7 @@ type Options struct { OCIClient oci.Client OutputPath string Owner string + Repo string Verbose bool } diff --git a/pkg/cmd/attestation/verification/attestation.go b/pkg/cmd/attestation/verification/attestation.go index e972a18ab..52ce752c3 100644 --- a/pkg/cmd/attestation/verification/attestation.go +++ b/pkg/cmd/attestation/verification/attestation.go @@ -31,7 +31,7 @@ func GetAttestations(c FetchAttestationsConfig) ([]*api.Attestation, error) { if c.IsBundleProvided() { return GetLocalAttestations(c.BundlePath) } - return getRemoteAttestations(c) + return GetRemoteAttestations(c) } // GetLocalAttestations returns a slice of attestations read from a local bundle file. @@ -92,7 +92,7 @@ func loadBundlesFromJSONLinesFile(path string) ([]*api.Attestation, error) { return attestations, nil } -func getRemoteAttestations(c FetchAttestationsConfig) ([]*api.Attestation, error) { +func GetRemoteAttestations(c FetchAttestationsConfig) ([]*api.Attestation, error) { if c.APIClient == nil { return nil, fmt.Errorf("api client must be provided") }