diff --git a/pkg/cmd/pr/checks/checks.go b/pkg/cmd/pr/checks/checks.go index 65df87b15..fb629d497 100644 --- a/pkg/cmd/pr/checks/checks.go +++ b/pkg/cmd/pr/checks/checks.go @@ -30,6 +30,7 @@ type ChecksOptions struct { WebMode bool Interval time.Duration Watch bool + FailFast bool Required bool } @@ -59,6 +60,10 @@ func NewCmdChecks(f *cmdutil.Factory, runF func(*ChecksOptions) error) *cobra.Co return cmdutil.FlagErrorf("argument required when using the `--repo` flag") } + if opts.FailFast && !opts.Watch { + return cmdutil.FlagErrorf("cannot use `--fail-fast` flag without `--watch` flag") + } + intervalChanged := cmd.Flags().Changed("interval") if !opts.Watch && intervalChanged { return cmdutil.FlagErrorf("cannot use `--interval` flag without `--watch` flag") @@ -86,6 +91,7 @@ func NewCmdChecks(f *cmdutil.Factory, runF func(*ChecksOptions) error) *cobra.Co cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the web browser to show details about checks") cmd.Flags().BoolVarP(&opts.Watch, "watch", "", false, "Watch checks until they finish") + cmd.Flags().BoolVarP(&opts.FailFast, "fail-fast", "", false, "Exit watch mode on first check failure") cmd.Flags().IntVarP(&interval, "interval", "i", 10, "Refresh interval in seconds when using `--watch` flag") cmd.Flags().BoolVar(&opts.Required, "required", false, "Only show checks that are required") @@ -171,6 +177,10 @@ func checksRun(opts *ChecksOptions) error { break } + if opts.FailFast && counts.Failed > 0 { + break + } + time.Sleep(opts.Interval) checks, counts, err = populateStatusChecks(client, repo, pr, opts.Required) diff --git a/pkg/cmd/pr/checks/checks_test.go b/pkg/cmd/pr/checks/checks_test.go index 3e5c8394a..186c1d324 100644 --- a/pkg/cmd/pr/checks/checks_test.go +++ b/pkg/cmd/pr/checks/checks_test.go @@ -62,6 +62,20 @@ func TestNewCmdChecks(t *testing.T) { cli: "--interval 5", wantsError: "cannot use `--interval` flag without `--watch` flag", }, + { + name: "watch with fail-fast flag", + cli: "--watch --fail-fast", + wants: ChecksOptions{ + Watch: true, + FailFast: true, + Interval: time.Duration(10000000000), + }, + }, + { + name: "fail-fast flag without watch flag", + cli: "--fail-fast", + wantsError: "cannot use `--fail-fast` flag without `--watch` flag", + }, { name: "required flag", cli: "--required", @@ -102,6 +116,7 @@ func TestNewCmdChecks(t *testing.T) { assert.Equal(t, tt.wants.Watch, gotOpts.Watch) assert.Equal(t, tt.wants.Interval, gotOpts.Interval) assert.Equal(t, tt.wants.Required, gotOpts.Required) + assert.Equal(t, tt.wants.FailFast, gotOpts.FailFast) }) } } @@ -111,6 +126,7 @@ func Test_checksRun(t *testing.T) { name string tty bool watch bool + failFast bool required bool httpStubs func(*httpmock.Registry) wantOut string @@ -189,6 +205,20 @@ func Test_checksRun(t *testing.T) { wantOut: "\x1b[?1049hAll checks were successful\n0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓ awesome tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n\x1b[?1049lAll checks were successful\n0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓ awesome tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n", wantErr: "", }, + { + name: "watch some failing with fail fast", + tty: true, + watch: true, + failFast: true, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query PullRequestStatusChecks\b`), + httpmock.FileResponse("./fixtures/someFailing.json"), + ) + }, + wantOut: "\x1b[?1049h\x1b[0;0H\x1b[JRefreshing checks status every 0 seconds. Press Ctrl+C to quit.\n\nSome checks were not successful\n1 failing, 1 successful, 0 skipped, and 1 pending checks\n\nX sad tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n* slow tests 1m26s sweet link\n\x1b[?1049lSome checks were not successful\n1 failing, 1 successful, 0 skipped, and 1 pending checks\n\nX sad tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n* slow tests 1m26s sweet link\n", + wantErr: "SilentError", + }, { name: "with statuses", tty: true, @@ -316,6 +346,7 @@ func Test_checksRun(t *testing.T) { SelectorArg: "123", Finder: shared.NewMockFinder("123", response, ghrepo.New("OWNER", "REPO")), Watch: tt.watch, + FailFast: tt.failFast, Required: tt.required, } @@ -381,7 +412,7 @@ func TestChecksRun_web(t *testing.T) { } } -func TestEliminateDupulicates(t *testing.T) { +func TestEliminateDuplicates(t *testing.T) { tests := []struct { name string checkContexts []api.CheckContext