diff --git a/pkg/cmd/run/cancel/cancel.go b/pkg/cmd/run/cancel/cancel.go index d73e46a68..3e9ef0071 100644 --- a/pkg/cmd/run/cancel/cancel.go +++ b/pkg/cmd/run/cancel/cancel.go @@ -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, diff --git a/pkg/cmd/run/delete/delete.go b/pkg/cmd/run/delete/delete.go index 711e98c02..329812019 100644 --- a/pkg/cmd/run/delete/delete.go +++ b/pkg/cmd/run/delete/delete.go @@ -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, diff --git a/pkg/cmd/run/download/download.go b/pkg/cmd/run/download/download.go index 6190325b9..521705815 100644 --- a/pkg/cmd/run/download/download.go +++ b/pkg/cmd/run/download/download.go @@ -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, diff --git a/pkg/cmd/run/download/http.go b/pkg/cmd/run/download/http.go index 09293b056..88d1509c0 100644 --- a/pkg/cmd/run/download/http.go +++ b/pkg/cmd/run/download/http.go @@ -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) } diff --git a/pkg/cmd/run/list/list.go b/pkg/cmd/run/list/list.go index e6475d34a..dee7a60c5 100644 --- a/pkg/cmd/run/list/list.go +++ b/pkg/cmd/run/list/list.go @@ -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, diff --git a/pkg/cmd/run/rerun/rerun.go b/pkg/cmd/run/rerun/rerun.go index 8777e0a8a..e200efc8b 100644 --- a/pkg/cmd/run/rerun/rerun.go +++ b/pkg/cmd/run/rerun/rerun.go @@ -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"` } diff --git a/pkg/cmd/run/run.go b/pkg/cmd/run/run.go index d62f03328..769a287a4 100644 --- a/pkg/cmd/run/run.go +++ b/pkg/cmd/run/run.go @@ -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 ", diff --git a/pkg/cmd/run/shared/artifacts.go b/pkg/cmd/run/shared/artifacts.go index e835958be..8491e8bcb 100644 --- a/pkg/cmd/run/shared/artifacts.go +++ b/pkg/cmd/run/shared/artifacts.go @@ -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 diff --git a/pkg/cmd/run/shared/presentation.go b/pkg/cmd/run/shared/presentation.go index 983979d8a..55bf1cca6 100644 --- a/pkg/cmd/run/shared/presentation.go +++ b/pkg/cmd/run/shared/presentation.go @@ -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{} diff --git a/pkg/cmd/run/shared/shared.go b/pkg/cmd/run/shared/shared.go index 8c191846a..8ed2db53e 100644 --- a/pkg/cmd/run/shared/shared.go +++ b/pkg/cmd/run/shared/shared.go @@ -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 { diff --git a/pkg/cmd/run/shared/test.go b/pkg/cmd/run/shared/test.go index 092067523..25a056b25 100644 --- a/pkg/cmd/run/shared/test.go +++ b/pkg/cmd/run/shared/test.go @@ -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) } diff --git a/pkg/cmd/run/view/logs.go b/pkg/cmd/run/view/logs.go index 8961381b3..6931bacb7 100644 --- a/pkg/cmd/run/view/logs.go +++ b/pkg/cmd/run/view/logs.go @@ -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 { diff --git a/pkg/cmd/run/view/view.go b/pkg/cmd/run/view/view.go index bed9e3bfa..cc2eba4eb 100644 --- a/pkg/cmd/run/view/view.go +++ b/pkg/cmd/run/view/view.go @@ -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, diff --git a/pkg/cmd/run/watch/watch.go b/pkg/cmd/run/watch/watch.go index a73a91e1a..37136b228 100644 --- a/pkg/cmd/run/watch/watch.go +++ b/pkg/cmd/run/watch/watch.go @@ -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,