diff --git a/pkg/cmd/run/watch/watch.go b/pkg/cmd/run/watch/watch.go index 86e71e88c..41162e3be 100644 --- a/pkg/cmd/run/watch/watch.go +++ b/pkg/cmd/run/watch/watch.go @@ -41,7 +41,7 @@ func NewCmdWatch(f *cmdutil.Factory, runF func(*WatchOptions) error) *cobra.Comm cmd := &cobra.Command{ Use: "watch ", - Short: "Runs until a run completes, showing its progress", + Short: "Watch a run until it completes, showing its progress", Annotations: map[string]string{ "IsActions": "true", }, @@ -82,11 +82,13 @@ func watchRun(opts *WatchOptions) error { return fmt.Errorf("failed to determine base repo: %w", err) } + out := opts.IO.Out + cs := opts.IO.ColorScheme() + runID := opts.RunID var run *shared.Run if opts.Prompt { - cs := opts.IO.ColorScheme() runs, err := shared.GetRunsWithFilter(client, repo, 10, func(run shared.Run) bool { return run.Status != shared.Completed }) @@ -115,6 +117,7 @@ func watchRun(opts *WatchOptions) error { } if run.Status == shared.Completed { + fmt.Fprintf(out, "Run %s (%s) has already completed with '%s'\n", cs.Bold(run.Name), cs.Cyanf("%d", run.ID), run.Conclusion) return nil } @@ -124,11 +127,9 @@ func watchRun(opts *WatchOptions) error { prNumber = fmt.Sprintf(" #%d", number) } - if runtime.GOOS == "windows" { - opts.IO.EnableVirtualTerminalProcessing() - } + opts.IO.EnableVirtualTerminalProcessing() // clear entire screen - fmt.Fprintf(opts.IO.Out, "\x1b[2J") + fmt.Fprintf(out, "\x1b[2J") annotationCache := map[int][]shared.Annotation{} @@ -145,6 +146,14 @@ func watchRun(opts *WatchOptions) error { time.Sleep(duration) } + symbol, symbolColor := shared.Symbol(cs, run.Status, run.Conclusion) + id := cs.Cyanf("%d", run.ID) + + if opts.IO.IsStdoutTTY() { + fmt.Fprintln(out) + fmt.Fprintf(out, "%s Run %s (%s) completed with '%s'\n", symbolColor(symbol), cs.Bold(run.Name), id, run.Conclusion) + } + if opts.ExitStatus && run.Conclusion != shared.Success { return cmdutil.SilentError } @@ -160,14 +169,14 @@ func renderRun(opts WatchOptions, client *api.Client, repo ghrepo.Interface, run run, err = shared.GetRun(client, repo, fmt.Sprintf("%d", run.ID)) if err != nil { - return run, fmt.Errorf("failed to get run: %w", err) + return nil, fmt.Errorf("failed to get run: %w", err) } ago := opts.Now().Sub(run.CreatedAt) jobs, err := shared.GetJobs(client, repo, *run) if err != nil { - return run, fmt.Errorf("failed to get jobs: %w", err) + return nil, fmt.Errorf("failed to get jobs: %w", err) } var annotations []shared.Annotation @@ -192,7 +201,7 @@ func renderRun(opts WatchOptions, client *api.Client, repo ghrepo.Interface, run } if annotationErr != nil { - return run, fmt.Errorf("failed to get annotations: %w", annotationErr) + return nil, fmt.Errorf("failed to get annotations: %w", annotationErr) } if runtime.GOOS == "windows" { @@ -210,7 +219,7 @@ func renderRun(opts WatchOptions, client *api.Client, repo ghrepo.Interface, run fmt.Fprintln(out, shared.RenderRunHeader(cs, *run, utils.FuzzyAgo(ago), prNumber)) fmt.Fprintln(out) - if len(jobs) == 0 && run.Conclusion == shared.Failure { + if len(jobs) == 0 { return run, nil } diff --git a/pkg/cmd/run/watch/watch_test.go b/pkg/cmd/run/watch/watch_test.go index b399dacf4..4152aad52 100644 --- a/pkg/cmd/run/watch/watch_test.go +++ b/pkg/cmd/run/watch/watch_test.go @@ -178,7 +178,6 @@ func TestWatchRun(t *testing.T) { onlyWindows bool skipWindows bool }{ - // TODO exit status respected { name: "run ID provided run already completed", opts: &WatchOptions{ @@ -189,10 +188,11 @@ func TestWatchRun(t *testing.T) { httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/1234"), httpmock.JSONResponse(shared.FailedRun)) }, - wantOut: "", + wantOut: "Run failed (1234) has already completed with 'failure'\n", }, { name: "prompt, no in progress runs", + tty: true, opts: &WatchOptions{ Prompt: true, }, @@ -212,6 +212,7 @@ func TestWatchRun(t *testing.T) { { name: "interval respected", skipWindows: true, + tty: true, opts: &WatchOptions{ Interval: 0, Prompt: true, @@ -220,7 +221,7 @@ func TestWatchRun(t *testing.T) { askStubs: func(as *prompt.AskStubber) { as.StubOne(1) }, - wantOut: "\x1b[2J\x1b[0;0H\x1b[JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\n- trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\n\x1b[0;0H\x1b[JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\n✓ trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\n", + wantOut: "\x1b[2J\x1b[0;0H\x1b[JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\n- trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\n\x1b[0;0H\x1b[JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\n✓ trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\n\n✓ Run more runs (2) completed with 'success'\n", }, { name: "interval respected, windows", @@ -237,6 +238,7 @@ func TestWatchRun(t *testing.T) { }, { name: "exit status respected", + tty: true, skipWindows: true, opts: &WatchOptions{ Interval: 0, @@ -247,7 +249,7 @@ func TestWatchRun(t *testing.T) { askStubs: func(as *prompt.AskStubber) { as.StubOne(1) }, - wantOut: "\x1b[2J\x1b[0;0H\x1b[JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\n- trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\n\n\x1b[0;0H\x1b[JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\nX trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n", + wantOut: "\x1b[2J\x1b[0;0H\x1b[JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\n- trunk more runs · 2\nTriggered via push about 59 minutes ago\n\n\x1b[0;0H\x1b[JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\nX trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n\nX Run more runs (2) completed with 'failure'\n", wantErr: true, errMsg: "SilentError", }, @@ -263,7 +265,7 @@ func TestWatchRun(t *testing.T) { askStubs: func(as *prompt.AskStubber) { as.StubOne(1) }, - wantOut: "\x1b[2J\x1b[2JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\n- trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\n\n\x1b[2JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\nX trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n", + wantOut: "\x1b[2J\x1b[2JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\n- trunk more runs · 2\nTriggered via push about 59 minutes ago\n\n\x1b[2JRefreshing run status every 0 seconds. Press Ctrl+C to quit.\n\nX trunk more runs · 2\nTriggered via push about 59 minutes ago\n\nJOBS\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n", wantErr: true, errMsg: "SilentError", },