Add godoc comments to exported symbols in pkg/cmd/run

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Kynan Ware 2026-03-04 15:53:48 -07:00
parent 34d1fb3e63
commit 844963ca61
14 changed files with 122 additions and 17 deletions

View file

@ -14,6 +14,7 @@ import (
"github.com/spf13/cobra"
)
// CancelOptions holds the options for the cancel command.
type CancelOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -26,6 +27,7 @@ type CancelOptions struct {
Force bool
}
// NewCmdCancel creates the cancel command.
func NewCmdCancel(f *cmdutil.Factory, runF func(*CancelOptions) error) *cobra.Command {
opts := &CancelOptions{
IO: f.IOStreams,

View file

@ -19,6 +19,7 @@ const (
defaultRunGetLimit = 10
)
// DeleteOptions holds the options for the delete command.
type DeleteOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -28,6 +29,7 @@ type DeleteOptions struct {
RunID string
}
// NewCmdDelete creates the delete command.
func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Command {
opts := &DeleteOptions{
IO: f.IOStreams,

View file

@ -14,6 +14,7 @@ import (
"github.com/spf13/cobra"
)
// DownloadOptions holds the options for the download command.
type DownloadOptions struct {
IO *iostreams.IOStreams
Platform platform
@ -35,6 +36,7 @@ type iprompter interface {
MultiSelect(string, []string, []string) ([]int, error)
}
// NewCmdDownload creates the download command.
func NewCmdDownload(f *cmdutil.Factory, runF func(*DownloadOptions) error) *cobra.Command {
opts := &DownloadOptions{
IO: f.IOStreams,

View file

@ -19,10 +19,12 @@ type apiPlatform struct {
repo ghrepo.Interface
}
// List returns all artifacts for the given run.
func (p *apiPlatform) List(runID string) ([]shared.Artifact, error) {
return shared.ListArtifacts(p.client, p.repo, runID)
}
// Download fetches and extracts an artifact to the given directory.
func (p *apiPlatform) Download(url string, dir safepaths.Absolute) error {
return downloadArtifact(p.client, url, dir)
}

View file

@ -21,6 +21,7 @@ const (
defaultLimit = 20
)
// ListOptions holds the options for the list command.
type ListOptions struct {
IO *iostreams.IOStreams
HttpClient func() (*http.Client, error)
@ -46,6 +47,7 @@ type iprompter interface {
Select(string, string, []string) (int, error)
}
// NewCmdList creates the list command.
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
opts := &ListOptions{
IO: f.IOStreams,

View file

@ -17,6 +17,7 @@ import (
"github.com/spf13/cobra"
)
// RerunOptions holds the options for the rerun command.
type RerunOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -31,6 +32,7 @@ type RerunOptions struct {
Prompt bool
}
// NewCmdRerun creates the rerun command.
func NewCmdRerun(f *cmdutil.Factory, runF func(*RerunOptions) error) *cobra.Command {
opts := &RerunOptions{
IO: f.IOStreams,
@ -228,6 +230,7 @@ func rerunJob(client *api.Client, repo ghrepo.Interface, job *shared.Job, debug
return nil
}
// RerunPayload is the request body for rerunning a workflow.
type RerunPayload struct {
Debug bool `json:"enable_debug_logging"`
}

View file

@ -12,6 +12,7 @@ import (
"github.com/spf13/cobra"
)
// NewCmdRun creates the run command group.
func NewCmdRun(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "run <command>",

View file

@ -11,6 +11,7 @@ import (
"github.com/cli/cli/v2/internal/ghrepo"
)
// Artifact represents a GitHub Actions workflow run artifact.
type Artifact struct {
Name string `json:"name"`
Size uint64 `json:"size_in_bytes"`
@ -22,6 +23,7 @@ type artifactsPayload struct {
Artifacts []Artifact
}
// ListArtifacts fetches all artifacts for a repository or a specific run.
func ListArtifacts(httpClient *http.Client, repo ghrepo.Interface, runID string) ([]Artifact, error) {
var results []Artifact

View file

@ -7,6 +7,7 @@ import (
"github.com/cli/cli/v2/pkg/iostreams"
)
// RenderRunHeader renders a header string for a workflow run.
func RenderRunHeader(cs *iostreams.ColorScheme, run Run, ago, prNumber string, attempt uint64) string {
title := fmt.Sprintf("%s %s%s",
cs.Bold(run.HeadBranch), run.WorkflowName(), prNumber)
@ -25,6 +26,7 @@ func RenderRunHeader(cs *iostreams.ColorScheme, run Run, ago, prNumber string, a
return header
}
// RenderJobs renders a list of jobs with their status and steps.
func RenderJobs(cs *iostreams.ColorScheme, jobs []Job, verbose bool) string {
lines := []string{}
for _, job := range jobs {
@ -47,6 +49,7 @@ func RenderJobs(cs *iostreams.ColorScheme, jobs []Job, verbose bool) string {
return strings.Join(lines, "\n")
}
// RenderJobsCompact renders a compact list of jobs showing only relevant steps.
func RenderJobsCompact(cs *iostreams.ColorScheme, jobs []Job) string {
lines := []string{}
for _, job := range jobs {
@ -89,6 +92,7 @@ func RenderJobsCompact(cs *iostreams.ColorScheme, jobs []Job) string {
return strings.Join(lines, "\n")
}
// RenderAnnotations renders a list of annotations with symbols and messages.
func RenderAnnotations(cs *iostreams.ColorScheme, annotations []Annotation) string {
lines := []string{}

View file

@ -15,38 +15,60 @@ import (
"github.com/cli/cli/v2/pkg/iostreams"
)
// Prompter provides interactive selection prompts.
type Prompter interface {
Select(string, string, []string) (int, error)
}
const (
// Run statuses
Queued Status = "queued"
Completed Status = "completed"
// Queued indicates a workflow run is queued.
Queued Status = "queued"
// Completed indicates a workflow run has finished.
Completed Status = "completed"
// InProgress indicates a workflow run is currently executing.
InProgress Status = "in_progress"
Requested Status = "requested"
Waiting Status = "waiting"
Pending Status = "pending"
// Requested indicates a workflow run has been requested.
Requested Status = "requested"
// Waiting indicates a workflow run is waiting to be processed.
Waiting Status = "waiting"
// Pending indicates a workflow run is pending.
Pending Status = "pending"
// Run conclusions
// ActionRequired indicates a workflow run requires action.
ActionRequired Conclusion = "action_required"
Cancelled Conclusion = "cancelled"
Failure Conclusion = "failure"
Neutral Conclusion = "neutral"
Skipped Conclusion = "skipped"
Stale Conclusion = "stale"
// Cancelled indicates a workflow run was cancelled.
Cancelled Conclusion = "cancelled"
// Failure indicates a workflow run failed.
Failure Conclusion = "failure"
// Neutral indicates a workflow run completed with a neutral result.
Neutral Conclusion = "neutral"
// Skipped indicates a workflow run was skipped.
Skipped Conclusion = "skipped"
// Stale indicates a workflow run became stale.
Stale Conclusion = "stale"
// StartupFailure indicates a workflow run failed during startup.
StartupFailure Conclusion = "startup_failure"
Success Conclusion = "success"
TimedOut Conclusion = "timed_out"
// Success indicates a workflow run completed successfully.
Success Conclusion = "success"
// TimedOut indicates a workflow run exceeded its time limit.
TimedOut Conclusion = "timed_out"
// AnnotationFailure represents a failure-level annotation.
AnnotationFailure Level = "failure"
// AnnotationWarning represents a warning-level annotation.
AnnotationWarning Level = "warning"
)
// Status represents the status of a workflow run or job.
type Status string
// Conclusion represents the conclusion of a workflow run or job.
type Conclusion string
// Level represents the severity level of an annotation.
type Level string
// AllStatuses contains all valid workflow run status and conclusion values.
var AllStatuses = []string{
"queued",
"completed",
@ -65,6 +87,7 @@ var AllStatuses = []string{
"timed_out",
}
// RunFields lists the available fields for exporting workflow run data.
var RunFields = []string{
"name",
"displayTitle",
@ -84,8 +107,10 @@ var RunFields = []string{
"url",
}
// SingleRunFields extends RunFields with additional fields available for a single run.
var SingleRunFields = append(RunFields, "jobs")
// Run represents a GitHub Actions workflow run.
type Run struct {
Name string `json:"name"` // the semantics of this field are unclear
DisplayTitle string `json:"display_title"`
@ -109,6 +134,7 @@ type Run struct {
Jobs []Job `json:"-"` // populated by GetJobs
}
// StartedTime returns the effective start time, falling back to CreatedAt.
func (r *Run) StartedTime() time.Time {
if r.StartedAt.IsZero() {
return r.CreatedAt
@ -116,6 +142,7 @@ func (r *Run) StartedTime() time.Time {
return r.StartedAt
}
// Duration returns the elapsed duration of the run.
func (r *Run) Duration(now time.Time) time.Duration {
endTime := r.UpdatedAt
if r.Status != Completed {
@ -128,6 +155,7 @@ func (r *Run) Duration(now time.Time) time.Duration {
return d.Round(time.Second)
}
// Repo represents a repository reference within a workflow run.
type Repo struct {
Owner struct {
Login string
@ -135,6 +163,7 @@ type Repo struct {
Name string
}
// Commit represents a Git commit associated with a workflow run.
type Commit struct {
Message string
}
@ -159,6 +188,7 @@ func (r Run) WorkflowName() string {
return r.workflowName
}
// ExportData returns the run data as a map for the given fields.
func (r *Run) ExportData(fields []string) map[string]interface{} {
v := reflect.ValueOf(r).Elem()
fieldByName := func(v reflect.Value, field string) reflect.Value {
@ -219,6 +249,7 @@ func (r *Run) ExportData(fields []string) map[string]interface{} {
return data
}
// Job represents a job within a GitHub Actions workflow run.
type Job struct {
ID int64
Status Status
@ -231,6 +262,7 @@ type Job struct {
RunID int64 `json:"run_id"`
}
// Step represents a step within a workflow job.
type Step struct {
Name string
Status Status
@ -240,12 +272,19 @@ type Step struct {
CompletedAt time.Time `json:"completed_at"`
}
// Steps is a sortable slice of Step values.
type Steps []Step
func (s Steps) Len() int { return len(s) }
func (s Steps) Less(i, j int) bool { return s[i].Number < s[j].Number }
func (s Steps) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// Len returns the number of steps.
func (s Steps) Len() int { return len(s) }
// Less reports whether step i should sort before step j.
func (s Steps) Less(i, j int) bool { return s[i].Number < s[j].Number }
// Swap exchanges steps at indices i and j.
func (s Steps) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// Annotation represents a check run annotation.
type Annotation struct {
JobName string
Message string
@ -254,6 +293,7 @@ type Annotation struct {
StartLine int `json:"start_line"`
}
// AnnotationSymbol returns the display symbol for an annotation based on its level.
func AnnotationSymbol(cs *iostreams.ColorScheme, a Annotation) string {
switch a.Level {
case AnnotationFailure:
@ -265,10 +305,12 @@ func AnnotationSymbol(cs *iostreams.ColorScheme, a Annotation) string {
}
}
// CheckRun represents a GitHub check run.
type CheckRun struct {
ID int64
}
// ErrMissingAnnotationsPermissions is returned when the token lacks permission to read annotations.
var ErrMissingAnnotationsPermissions = errors.New("missing annotations permissions error")
// GetAnnotations fetches annotations from the REST API.
@ -310,6 +352,7 @@ func GetAnnotations(client *api.Client, repo ghrepo.Interface, job Job) ([]Annot
return out, nil
}
// IsFailureState reports whether the conclusion represents a failure.
func IsFailureState(c Conclusion) bool {
switch c {
case ActionRequired, Failure, StartupFailure, TimedOut:
@ -319,15 +362,18 @@ func IsFailureState(c Conclusion) bool {
}
}
// IsSkipped reports whether the conclusion is skipped.
func IsSkipped(c Conclusion) bool {
return c == Skipped
}
// RunsPayload is the API response for listing workflow runs.
type RunsPayload struct {
TotalCount int `json:"total_count"`
WorkflowRuns []Run `json:"workflow_runs"`
}
// FilterOptions specifies filters for querying workflow runs.
type FilterOptions struct {
Branch string
Actor string
@ -360,6 +406,7 @@ func GetRunsWithFilter(client *api.Client, repo ghrepo.Interface, opts *FilterOp
return filtered, nil
}
// GetRuns fetches workflow runs from the API with the given filters and limit.
func GetRuns(client *api.Client, repo ghrepo.Interface, opts *FilterOptions, limit int) (*RunsPayload, error) {
path := fmt.Sprintf("repos/%s/actions/runs", ghrepo.FullName(repo))
if opts != nil && opts.WorkflowID > 0 {
@ -470,11 +517,13 @@ func preloadWorkflowNames(client *api.Client, repo ghrepo.Interface, runs []Run)
return nil
}
// JobsPayload is the API response for listing workflow jobs.
type JobsPayload struct {
TotalCount int `json:"total_count"`
Jobs []Job
}
// GetJobs fetches the jobs for a workflow run.
func GetJobs(client *api.Client, repo ghrepo.Interface, run *Run, attempt uint64) ([]Job, error) {
if run.Jobs != nil {
return run.Jobs, nil
@ -502,6 +551,7 @@ func GetJobs(client *api.Client, repo ghrepo.Interface, run *Run, attempt uint64
return run.Jobs, nil
}
// GetJob fetches a single job by its ID.
func GetJob(client *api.Client, repo ghrepo.Interface, jobID string) (*Job, error) {
path := fmt.Sprintf("repos/%s/actions/jobs/%s", ghrepo.FullName(repo), jobID)
@ -536,6 +586,7 @@ func SelectRun(p Prompter, cs *iostreams.ColorScheme, runs []Run) (string, error
return fmt.Sprintf("%d", runs[selected].ID), nil
}
// GetRun fetches a single workflow run by its ID and optional attempt number.
func GetRun(client *api.Client, repo ghrepo.Interface, runID string, attempt uint64) (*Run, error) {
var result Run
@ -570,6 +621,7 @@ func GetRun(client *api.Client, repo ghrepo.Interface, runID string, attempt uin
type colorFunc func(string) string
// Symbol returns a display symbol and color function for a given status and conclusion.
func Symbol(cs *iostreams.ColorScheme, status Status, conclusion Conclusion) (string, colorFunc) {
noColor := func(s string) string { return s }
if status == Completed {
@ -586,6 +638,7 @@ func Symbol(cs *iostreams.ColorScheme, status Status, conclusion Conclusion) (st
return "*", cs.Yellow
}
// PullRequestForRun finds the pull request number associated with a workflow run.
func PullRequestForRun(client *api.Client, repo ghrepo.Interface, run Run) (int, error) {
type response struct {
Repository struct {

View file

@ -8,20 +8,25 @@ import (
"github.com/cli/cli/v2/pkg/iostreams"
)
// TestRunStartTime is the default start time used for test runs.
var TestRunStartTime, _ = time.Parse("2006-01-02 15:04:05", "2021-02-23 04:51:00")
// TestRun creates a Run with the given ID, status, and conclusion for testing.
func TestRun(id int64, s Status, c Conclusion) Run {
return TestRunWithCommit(id, s, c, "cool commit")
}
// TestRunWithCommit creates a test Run with a specified commit message.
func TestRunWithCommit(id int64, s Status, c Conclusion, commit string) Run {
return TestRunWithWorkflowAndCommit(123, id, s, c, commit)
}
// TestRunWithOrgRequiredWorkflow creates a test Run for an organization required workflow.
func TestRunWithOrgRequiredWorkflow(id int64, s Status, c Conclusion, commit string) Run {
return TestRunWithWorkflowAndCommit(456, id, s, c, commit)
}
// TestRunWithWorkflowAndCommit creates a test Run with a specific workflow ID and commit.
func TestRunWithWorkflowAndCommit(workflowId, runId int64, s Status, c Conclusion, commit string) Run {
return Run{
WorkflowID: workflowId,
@ -45,9 +50,13 @@ func TestRunWithWorkflowAndCommit(workflowId, runId int64, s Status, c Conclusio
}
}
// SuccessfulRun is a test Run that completed successfully.
var SuccessfulRun Run = TestRun(3, Completed, Success)
// FailedRun is a test Run that completed with a failure.
var FailedRun Run = TestRun(1234, Completed, Failure)
// TestRuns is a collection of test runs covering various statuses and conclusions.
var TestRuns []Run = []Run{
TestRun(1, Completed, TimedOut),
TestRun(2, InProgress, ""),
@ -61,6 +70,7 @@ var TestRuns []Run = []Run{
TestRun(10, Completed, Stale),
}
// TestRunsWithOrgRequiredWorkflows is a collection of test runs including org required workflows.
var TestRunsWithOrgRequiredWorkflows []Run = []Run{
TestRunWithOrgRequiredWorkflow(1, Completed, TimedOut, "cool commit"),
TestRunWithOrgRequiredWorkflow(2, InProgress, "", "cool commit"),
@ -73,12 +83,14 @@ var TestRunsWithOrgRequiredWorkflows []Run = []Run{
TestRun(9, Queued, ""),
}
// WorkflowRuns is a subset of test runs for workflow-specific testing.
var WorkflowRuns []Run = []Run{
TestRun(2, InProgress, ""),
SuccessfulRun,
FailedRun,
}
// SuccessfulJob is a test Job that completed successfully.
var SuccessfulJob Job = Job{
ID: 10,
Status: Completed,
@ -158,6 +170,7 @@ var LegacySuccessfulJobWithoutStepLogs Job = Job{
},
}
// SkippedJob is a test Job that was skipped.
var SkippedJob Job = Job{
ID: 13,
Status: Completed,
@ -170,6 +183,7 @@ var SkippedJob Job = Job{
Steps: []Step{},
}
// FailedJob is a test Job that completed with a failure.
var FailedJob Job = Job{
ID: 20,
Status: Completed,
@ -249,6 +263,7 @@ var LegacyFailedJobWithoutStepLogs Job = Job{
},
}
// SuccessfulJobAnnotations contains test annotations for a successful job.
var SuccessfulJobAnnotations []Annotation = []Annotation{
{
JobName: "cool job",
@ -259,6 +274,7 @@ var SuccessfulJobAnnotations []Annotation = []Annotation{
},
}
// FailedJobAnnotations contains test annotations for a failed job.
var FailedJobAnnotations []Annotation = []Annotation{
{
JobName: "sad job",
@ -269,24 +285,29 @@ var FailedJobAnnotations []Annotation = []Annotation{
},
}
// TestWorkflow is a test Workflow used in test fixtures.
var TestWorkflow workflowShared.Workflow = workflowShared.Workflow{
Name: "CI",
ID: 123,
}
// TestExporter is a mock exporter for testing data export functionality.
type TestExporter struct {
fields []string
writeHandler func(io *iostreams.IOStreams, data interface{}) error
}
// MakeTestExporter creates a TestExporter with the given fields and write handler.
func MakeTestExporter(fields []string, wh func(io *iostreams.IOStreams, data interface{}) error) *TestExporter {
return &TestExporter{fields: fields, writeHandler: wh}
}
// Fields returns the export field names.
func (t *TestExporter) Fields() []string {
return t.fields
}
// Write writes the exported data using the configured handler.
func (t *TestExporter) Write(io *iostreams.IOStreams, data interface{}) error {
return t.writeHandler(io, data)
}

View file

@ -26,6 +26,7 @@ type zipLogFetcher struct {
File *zip.File
}
// GetLog opens and returns the log contents from the ZIP file entry.
func (f *zipLogFetcher) GetLog() (io.ReadCloser, error) {
return f.File.Open()
}
@ -37,6 +38,7 @@ type apiLogFetcher struct {
jobID int64
}
// GetLog fetches the log contents from the GitHub Actions API.
func (f *apiLogFetcher) GetLog() (io.ReadCloser, error) {
logURL := fmt.Sprintf("%srepos/%s/actions/jobs/%d/logs",
ghinstance.RESTPrefix(f.repo.RepoHost()), ghrepo.FullName(f.repo), f.jobID)
@ -235,6 +237,7 @@ func getZipLogMap(rlz *zip.Reader, jobs []shared.Job) *zipLogMap {
return zlm
}
// JOB_NAME_MAX_LENGTH is the maximum length of a job name in log filenames.
const JOB_NAME_MAX_LENGTH = 90
func getJobNameForLogFilename(name string) string {

View file

@ -25,10 +25,12 @@ import (
"github.com/spf13/cobra"
)
// RunLogCache provides caching for workflow run log ZIP archives.
type RunLogCache struct {
cacheDir string
}
// Exists reports whether a cache entry with the given key exists.
func (c RunLogCache) Exists(key string) (bool, error) {
_, err := os.Stat(c.filepath(key))
if err == nil {
@ -42,6 +44,7 @@ func (c RunLogCache) Exists(key string) (bool, error) {
return false, fmt.Errorf("checking cache entry: %v", err)
}
// Create stores content in the cache under the given key.
func (c RunLogCache) Create(key string, content io.Reader) error {
if err := os.MkdirAll(c.cacheDir, 0755); err != nil {
return fmt.Errorf("creating cache directory: %v", err)
@ -61,6 +64,7 @@ func (c RunLogCache) Create(key string, content io.Reader) error {
return nil
}
// Open returns the cached ZIP archive for the given key.
func (c RunLogCache) Open(key string) (*zip.ReadCloser, error) {
r, err := zip.OpenReader(c.filepath(key))
if err != nil {
@ -74,6 +78,7 @@ func (c RunLogCache) filepath(key string) string {
return filepath.Join(c.cacheDir, fmt.Sprintf("run-log-%s.zip", key))
}
// ViewOptions holds the options for the view command.
type ViewOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -97,6 +102,7 @@ type ViewOptions struct {
Now func() time.Time
}
// NewCmdView creates the view command.
func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Command {
opts := &ViewOptions{
IO: f.IOStreams,

View file

@ -19,6 +19,7 @@ import (
const defaultInterval int = 3
// WatchOptions holds the options for the watch command.
type WatchOptions struct {
IO *iostreams.IOStreams
HttpClient func() (*http.Client, error)
@ -35,6 +36,7 @@ type WatchOptions struct {
Now func() time.Time
}
// NewCmdWatch creates the watch command.
func NewCmdWatch(f *cmdutil.Factory, runF func(*WatchOptions) error) *cobra.Command {
opts := &WatchOptions{
IO: f.IOStreams,