diff --git a/pkg/cmd/release/create/create.go b/pkg/cmd/release/create/create.go index 8771b2477..c4f30698f 100644 --- a/pkg/cmd/release/create/create.go +++ b/pkg/cmd/release/create/create.go @@ -27,6 +27,7 @@ type iprompter interface { Confirm(string, bool) (bool, error) } +// CreateOptions holds the configuration for the release create command. type CreateOptions struct { IO *iostreams.IOStreams Config func() (gh.Config, error) @@ -63,6 +64,7 @@ type CreateOptions struct { FailOnNoCommits bool } +// NewCmdCreate creates a new cobra command for creating a GitHub release. func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command { opts := &CreateOptions{ IO: f.IOStreams, diff --git a/pkg/cmd/release/create/http.go b/pkg/cmd/release/create/http.go index 4311a3896..f793da83d 100644 --- a/pkg/cmd/release/create/http.go +++ b/pkg/cmd/release/create/http.go @@ -36,6 +36,7 @@ type errMissingRequiredWorkflowScope struct { Hostname string } +// Error returns the error message for a missing workflow scope. func (e errMissingRequiredWorkflowScope) Error() string { return "workflow scope may be required" } diff --git a/pkg/cmd/release/delete-asset/delete_asset.go b/pkg/cmd/release/delete-asset/delete_asset.go index b2e1f22fe..030837722 100644 --- a/pkg/cmd/release/delete-asset/delete_asset.go +++ b/pkg/cmd/release/delete-asset/delete_asset.go @@ -17,6 +17,7 @@ type iprompter interface { Confirm(string, bool) (bool, error) } +// DeleteAssetOptions holds the configuration for the release delete-asset command. type DeleteAssetOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams @@ -28,6 +29,7 @@ type DeleteAssetOptions struct { AssetName string } +// NewCmdDeleteAsset creates a new cobra command for deleting a release asset. func NewCmdDeleteAsset(f *cmdutil.Factory, runF func(*DeleteAssetOptions) error) *cobra.Command { opts := &DeleteAssetOptions{ IO: f.IOStreams, diff --git a/pkg/cmd/release/delete/delete.go b/pkg/cmd/release/delete/delete.go index 622b18893..1d4b027bc 100644 --- a/pkg/cmd/release/delete/delete.go +++ b/pkg/cmd/release/delete/delete.go @@ -19,6 +19,7 @@ type iprompter interface { Confirm(string, bool) (bool, error) } +// DeleteOptions holds the configuration for the release delete command. type DeleteOptions struct { HttpClient func() (*http.Client, error) GitClient *git.Client @@ -32,6 +33,7 @@ type DeleteOptions struct { CleanupTag bool } +// NewCmdDelete creates a new cobra command for deleting a GitHub release. func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Command { opts := &DeleteOptions{ IO: f.IOStreams, diff --git a/pkg/cmd/release/download/download.go b/pkg/cmd/release/download/download.go index b90721412..a45ba6a03 100644 --- a/pkg/cmd/release/download/download.go +++ b/pkg/cmd/release/download/download.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" ) +// DownloadOptions holds the configuration for the release download command. type DownloadOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams @@ -40,6 +41,7 @@ type DownloadOptions struct { ArchiveType string } +// NewCmdDownload creates a new cobra command for downloading release assets. func NewCmdDownload(f *cmdutil.Factory, runF func(*DownloadOptions) error) *cobra.Command { opts := &DownloadOptions{ IO: f.IOStreams, diff --git a/pkg/cmd/release/edit/edit.go b/pkg/cmd/release/edit/edit.go index 95abf5e20..fa1f12e81 100644 --- a/pkg/cmd/release/edit/edit.go +++ b/pkg/cmd/release/edit/edit.go @@ -13,6 +13,7 @@ import ( "github.com/spf13/cobra" ) +// EditOptions holds the configuration for the release edit command. type EditOptions struct { IO *iostreams.IOStreams HttpClient func() (*http.Client, error) @@ -29,6 +30,7 @@ type EditOptions struct { VerifyTag bool } +// NewCmdEdit creates a new cobra command for editing an existing GitHub release. func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Command { opts := &EditOptions{ IO: f.IOStreams, diff --git a/pkg/cmd/release/list/http.go b/pkg/cmd/release/list/http.go index d224f2f68..c5315259c 100644 --- a/pkg/cmd/release/list/http.go +++ b/pkg/cmd/release/list/http.go @@ -23,6 +23,7 @@ var releaseFields = []string{ "publishedAt", } +// Release represents a GitHub release as returned by the list query. type Release struct { Name string TagName string @@ -34,6 +35,7 @@ type Release struct { PublishedAt time.Time } +// ExportData returns the release data for the specified fields as a map. func (r *Release) ExportData(fields []string) map[string]interface{} { return cmdutil.StructExportData(r, fields) } diff --git a/pkg/cmd/release/list/list.go b/pkg/cmd/release/list/list.go index 897100562..95740452f 100644 --- a/pkg/cmd/release/list/list.go +++ b/pkg/cmd/release/list/list.go @@ -15,6 +15,7 @@ import ( "github.com/spf13/cobra" ) +// ListOptions holds the configuration for the release list command. type ListOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams @@ -29,6 +30,7 @@ type ListOptions struct { Order string } +// NewCmdList creates a new cobra command for listing GitHub releases. func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command { opts := &ListOptions{ IO: f.IOStreams, diff --git a/pkg/cmd/release/release.go b/pkg/cmd/release/release.go index f56042c81..ebdb690dd 100644 --- a/pkg/cmd/release/release.go +++ b/pkg/cmd/release/release.go @@ -15,6 +15,7 @@ import ( "github.com/spf13/cobra" ) +// NewCmdRelease creates the top-level cobra command for managing GitHub releases. func NewCmdRelease(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "release ", diff --git a/pkg/cmd/release/shared/attestation.go b/pkg/cmd/release/shared/attestation.go index 65990290b..a0db513cf 100644 --- a/pkg/cmd/release/shared/attestation.go +++ b/pkg/cmd/release/shared/attestation.go @@ -17,11 +17,13 @@ import ( "google.golang.org/protobuf/encoding/protojson" ) +// Verifier defines the interface for verifying release attestations. type Verifier interface { // VerifyAttestation verifies the attestation for a given artifact VerifyAttestation(art *artifact.DigestedArtifact, att *api.Attestation) (*verification.AttestationProcessingResult, error) } +// AttestationVerifier verifies release attestations using sigstore. type AttestationVerifier struct { AttClient api.Client HttpClient *http.Client @@ -29,6 +31,7 @@ type AttestationVerifier struct { TrustedRoot string } +// VerifyAttestation verifies a single attestation against the given artifact. func (v *AttestationVerifier) VerifyAttestation(art *artifact.DigestedArtifact, att *api.Attestation) (*verification.AttestationProcessingResult, error) { td, err := v.AttClient.GetTrustDomain() if err != nil { @@ -55,6 +58,7 @@ func (v *AttestationVerifier) VerifyAttestation(art *artifact.DigestedArtifact, return sigstoreVerified[0], nil } +// FilterAttestationsByTag returns only attestations whose predicate tag matches tagName. func FilterAttestationsByTag(attestations []*api.Attestation, tagName string) ([]*api.Attestation, error) { var filtered []*api.Attestation for _, att := range attestations { @@ -73,6 +77,7 @@ func FilterAttestationsByTag(attestations []*api.Attestation, tagName string) ([ return filtered, nil } +// FilterAttestationsByFileDigest returns only attestations whose subject digest matches fileDigest. func FilterAttestationsByFileDigest(attestations []*api.Attestation, fileDigest string) ([]*api.Attestation, error) { var filtered []*api.Attestation for _, att := range attestations { @@ -113,14 +118,17 @@ func buildVerificationPolicy(a artifact.DigestedArtifact, trustDomain string) ve return verify.NewPolicy(artifactDigestPolicyOption, verify.WithCertificateIdentity(certId)) } +// MockVerifier is a test double that implements the Verifier interface. type MockVerifier struct { mockResult *verification.AttestationProcessingResult } +// NewMockVerifier creates a MockVerifier that returns the given result. func NewMockVerifier(mockResult *verification.AttestationProcessingResult) *MockVerifier { return &MockVerifier{mockResult: mockResult} } +// VerifyAttestation returns the preconfigured mock result. func (v *MockVerifier) VerifyAttestation(art *artifact.DigestedArtifact, att *api.Attestation) (*verification.AttestationProcessingResult, error) { return &verification.AttestationProcessingResult{ Attestation: &api.Attestation{ diff --git a/pkg/cmd/release/shared/fetch.go b/pkg/cmd/release/shared/fetch.go index e1f155d04..b78859e25 100644 --- a/pkg/cmd/release/shared/fetch.go +++ b/pkg/cmd/release/shared/fetch.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" ) +// ReleaseFields lists the exportable field names for a Release. var ReleaseFields = []string{ "apiUrl", "author", @@ -41,6 +42,7 @@ var ReleaseFields = []string{ "zipballUrl", } +// Release represents a GitHub release with its metadata and assets. type Release struct { DatabaseID int64 `json:"id"` ID string `json:"node_id"` @@ -68,6 +70,7 @@ type Release struct { } } +// ReleaseAsset represents a single asset attached to a GitHub release. type ReleaseAsset struct { ID string `json:"node_id"` Name string @@ -84,6 +87,7 @@ type ReleaseAsset struct { BrowserDownloadURL string `json:"browser_download_url"` } +// ExportData returns the release data for the specified fields as a map. func (rel *Release) ExportData(fields []string) map[string]interface{} { v := reflect.ValueOf(rel).Elem() fieldByName := func(v reflect.Value, field string) reflect.Value { @@ -128,6 +132,7 @@ func (rel *Release) ExportData(fields []string) map[string]interface{} { return data } +// ErrReleaseNotFound is returned when the requested release does not exist. var ErrReleaseNotFound = errors.New("release not found") type fetchResult struct { @@ -135,6 +140,7 @@ type fetchResult struct { error error } +// FetchRefSHA retrieves the commit SHA for the given tag from the repository. func FetchRefSHA(ctx context.Context, httpClient *http.Client, repo ghrepo.Interface, tagName string) (string, error) { path := fmt.Sprintf("repos/%s/%s/git/ref/tags/%s", repo.RepoOwner(), repo.RepoName(), tagName) req, err := http.NewRequestWithContext(ctx, "GET", ghinstance.RESTPrefix(repo.RepoHost())+path, nil) @@ -265,6 +271,7 @@ func fetchReleasePath(ctx context.Context, httpClient *http.Client, host string, return &release, nil } +// StubFetchRelease registers mock HTTP handlers for fetching a release in tests. func StubFetchRelease(t *testing.T, reg *httpmock.Registry, owner, repoName, tagName, responseBody string) { path := "repos/OWNER/REPO/releases/tags/v1.2.3" if tagName == "" { @@ -286,6 +293,7 @@ func StubFetchRelease(t *testing.T, reg *httpmock.Registry, owner, repoName, tag } } +// StubFetchRefSHA registers a mock HTTP handler for fetching a ref SHA in tests. func StubFetchRefSHA(t *testing.T, reg *httpmock.Registry, owner, repoName, tagName, sha string) { path := fmt.Sprintf("repos/%s/%s/git/ref/tags/%s", owner, repoName, tagName) reg.Register( diff --git a/pkg/cmd/release/shared/upload.go b/pkg/cmd/release/shared/upload.go index ab7533320..22539bcd6 100644 --- a/pkg/cmd/release/shared/upload.go +++ b/pkg/cmd/release/shared/upload.go @@ -25,6 +25,7 @@ type httpDoer interface { type errNetwork struct{ error } +// AssetForUpload describes a file to be uploaded as a release asset. type AssetForUpload struct { Name string Label string @@ -36,6 +37,7 @@ type AssetForUpload struct { ExistingURL string } +// AssetsFromArgs parses command-line arguments into a list of assets for upload. func AssetsFromArgs(args []string) (assets []*AssetForUpload, err error) { labeledArgs, unlabeledArgs := cmdutil.Partition(args, func(arg string) bool { return strings.Contains(arg, "#") @@ -111,6 +113,7 @@ func fileExt(fn string) string { return path.Ext(fn) } +// ConcurrentUpload uploads release assets in parallel using the specified number of workers. func ConcurrentUpload(httpClient httpDoer, uploadURL string, numWorkers int, assets []*AssetForUpload) error { if numWorkers == 0 { return errors.New("the number of concurrent workers needs to be greater than 0") diff --git a/pkg/cmd/release/upload/upload.go b/pkg/cmd/release/upload/upload.go index 827dcdc64..1a17ad793 100644 --- a/pkg/cmd/release/upload/upload.go +++ b/pkg/cmd/release/upload/upload.go @@ -16,6 +16,7 @@ import ( "github.com/spf13/cobra" ) +// UploadOptions holds the configuration for the release upload command. type UploadOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams @@ -29,6 +30,7 @@ type UploadOptions struct { OverwriteExisting bool } +// NewCmdUpload creates a new cobra command for uploading assets to a release. func NewCmdUpload(f *cmdutil.Factory, runF func(*UploadOptions) error) *cobra.Command { opts := &UploadOptions{ IO: f.IOStreams, diff --git a/pkg/cmd/release/verify-asset/verify_asset.go b/pkg/cmd/release/verify-asset/verify_asset.go index acd8a134e..9d74ae495 100644 --- a/pkg/cmd/release/verify-asset/verify_asset.go +++ b/pkg/cmd/release/verify-asset/verify_asset.go @@ -19,6 +19,7 @@ import ( "github.com/spf13/cobra" ) +// VerifyAssetOptions holds the options for the release verify-asset command. type VerifyAssetOptions struct { TagName string BaseRepo ghrepo.Interface @@ -27,6 +28,7 @@ type VerifyAssetOptions struct { TrustedRoot string } +// VerifyAssetConfig holds the runtime dependencies for the verify-asset command. type VerifyAssetConfig struct { HttpClient *http.Client IO *iostreams.IOStreams @@ -35,6 +37,7 @@ type VerifyAssetConfig struct { AttVerifier shared.Verifier } +// NewCmdVerifyAsset creates a new cobra command for verifying a release asset attestation. func NewCmdVerifyAsset(f *cmdutil.Factory, runF func(*VerifyAssetConfig) error) *cobra.Command { opts := &VerifyAssetOptions{} diff --git a/pkg/cmd/release/verify/verify.go b/pkg/cmd/release/verify/verify.go index 65516764e..c77269444 100644 --- a/pkg/cmd/release/verify/verify.go +++ b/pkg/cmd/release/verify/verify.go @@ -22,6 +22,7 @@ import ( "github.com/spf13/cobra" ) +// VerifyOptions holds the options for the release verify command. type VerifyOptions struct { TagName string BaseRepo ghrepo.Interface @@ -29,6 +30,7 @@ type VerifyOptions struct { TrustedRoot string } +// VerifyConfig holds the runtime dependencies for the release verify command. type VerifyConfig struct { HttpClient *http.Client IO *iostreams.IOStreams @@ -37,6 +39,7 @@ type VerifyConfig struct { AttVerifier shared.Verifier } +// NewCmdVerify creates a new cobra command for verifying a release attestation. func NewCmdVerify(f *cmdutil.Factory, runF func(config *VerifyConfig) error) *cobra.Command { opts := &VerifyOptions{} diff --git a/pkg/cmd/release/view/view.go b/pkg/cmd/release/view/view.go index 4e88bf3aa..ae34c6487 100644 --- a/pkg/cmd/release/view/view.go +++ b/pkg/cmd/release/view/view.go @@ -23,6 +23,7 @@ type browser interface { Browse(string) error } +// ViewOptions holds the configuration for the release view command. type ViewOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams @@ -34,6 +35,7 @@ type ViewOptions struct { WebMode bool } +// NewCmdView creates a new cobra command for viewing a GitHub release. func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Command { opts := &ViewOptions{ IO: f.IOStreams,