From 8a7d80864bf107bf80f1f7c32f6d4c1b9ae8526e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Grumb=C3=B6ck?= Date: Thu, 6 Jul 2023 07:38:44 +0200 Subject: [PATCH] Add check description to pr checks output (#7451) --- api/queries_pr.go | 4 +- api/query_builder.go | 4 +- pkg/cmd/pr/checks/aggregate.go | 2 + pkg/cmd/pr/checks/checks_test.go | 126 ++++++++++++++---- .../pr/checks/fixtures/withDescriptions.json | 48 +++++++ pkg/cmd/pr/checks/output.go | 27 ++-- 6 files changed, 167 insertions(+), 44 deletions(-) create mode 100644 pkg/cmd/pr/checks/fixtures/withDescriptions.json diff --git a/api/queries_pr.go b/api/queries_pr.go index 982e79d62..36f3c2eef 100644 --- a/api/queries_pr.go +++ b/api/queries_pr.go @@ -196,8 +196,8 @@ type CheckContext struct { DetailsURL string `json:"detailsUrl"` /* StatusContext fields */ - - Context string `json:"context"` + Context string `json:"context"` + Description string `json:"description"` // EXPECTED ERROR FAILURE PENDING SUCCESS State StatusState `json:"state"` TargetURL string `json:"targetUrl"` diff --git a/api/query_builder.go b/api/query_builder.go index 47b2cdfc9..b74f9519b 100644 --- a/api/query_builder.go +++ b/api/query_builder.go @@ -184,7 +184,8 @@ func StatusCheckRollupGraphQLWithoutCountByState(after string) string { context, state, targetUrl, - createdAt + createdAt, + description }, ...on CheckRun { name, @@ -222,6 +223,7 @@ func RequiredStatusCheckRollupGraphQL(prID, after string) string { state, targetUrl, createdAt, + description, isRequired(pullRequestId: %[2]s) }, ...on CheckRun { diff --git a/pkg/cmd/pr/checks/aggregate.go b/pkg/cmd/pr/checks/aggregate.go index f6dd036ad..f9f0b73cb 100644 --- a/pkg/cmd/pr/checks/aggregate.go +++ b/pkg/cmd/pr/checks/aggregate.go @@ -17,6 +17,7 @@ type check struct { Bucket string `json:"bucket"` Event string `json:"event"` Workflow string `json:"workflow"` + Description string `json:"description"` } type checkCounts struct { @@ -59,6 +60,7 @@ func aggregateChecks(checkContexts []api.CheckContext, requiredChecks bool) (che Link: link, Event: c.CheckSuite.WorkflowRun.Event, Workflow: c.CheckSuite.WorkflowRun.Workflow.Name, + Description: c.Description, } switch state { diff --git a/pkg/cmd/pr/checks/checks_test.go b/pkg/cmd/pr/checks/checks_test.go index ebdf52306..a043176aa 100644 --- a/pkg/cmd/pr/checks/checks_test.go +++ b/pkg/cmd/pr/checks/checks_test.go @@ -133,7 +133,7 @@ func Test_checksRun(t *testing.T) { wantErr string }{ { - name: "no commits", + name: "no commits tty", tty: true, httpStubs: func(reg *httpmock.Registry) { reg.Register( @@ -145,7 +145,7 @@ func Test_checksRun(t *testing.T) { wantErr: "no commit found on the pull request", }, { - name: "no checks", + name: "no checks tty", tty: true, httpStubs: func(reg *httpmock.Registry) { reg.Register( @@ -157,7 +157,7 @@ func Test_checksRun(t *testing.T) { wantErr: "no checks reported on the 'trunk' branch", }, { - name: "some failing", + name: "some failing tty", tty: true, httpStubs: func(reg *httpmock.Registry) { reg.Register( @@ -165,11 +165,11 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/someFailing.json"), ) }, - wantOut: "Some 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", + wantOut: "Some 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: "some pending", + name: "some pending tty", tty: true, httpStubs: func(reg *httpmock.Registry) { reg.Register( @@ -177,11 +177,11 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/somePending.json"), ) }, - wantOut: "Some checks are still pending\n0 failing, 2 successful, 0 skipped, and 1 pending checks\n\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n* slow tests 1m26s sweet link\n", + wantOut: "Some checks are still pending\n0 failing, 2 successful, 0 skipped, and 1 pending checks\n\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n* slow tests 1m26s sweet link\n", wantErr: "SilentError", }, { - name: "all passing", + name: "all passing tty", tty: true, httpStubs: func(reg *httpmock.Registry) { reg.Register( @@ -189,11 +189,11 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/allPassing.json"), ) }, - wantOut: "All 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", + wantOut: "All 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 all passing", + name: "watch all passing tty", tty: true, watch: true, httpStubs: func(reg *httpmock.Registry) { @@ -202,11 +202,11 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/allPassing.json"), ) }, - 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", + 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", + name: "watch some failing with fail fast tty", tty: true, watch: true, failFast: true, @@ -216,11 +216,11 @@ func Test_checksRun(t *testing.T) { 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", + 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", + name: "with statuses tty", tty: true, httpStubs: func(reg *httpmock.Registry) { reg.Register( @@ -228,9 +228,20 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/withStatuses.json"), ) }, - wantOut: "Some checks were not successful\n1 failing, 2 successful, 0 skipped, and 0 pending checks\n\nX a status sweet link\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n", + wantOut: "Some checks were not successful\n1 failing, 2 successful, 0 skipped, and 0 pending checks\n\nX a status sweet link\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n", wantErr: "SilentError", }, + { + name: "no commits", + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query PullRequestStatusChecks\b`), + httpmock.StringResponse(`{"data":{"node":{}}}`), + ) + }, + wantOut: "", + wantErr: "no commit found on the pull request", + }, { name: "no checks", httpStubs: func(reg *httpmock.Registry) { @@ -250,7 +261,7 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/someFailing.json"), ) }, - wantOut: "sad tests\tfail\t1m26s\tsweet link\ncool tests\tpass\t1m26s\tsweet link\nslow tests\tpending\t1m26s\tsweet link\n", + wantOut: "sad tests\tfail\t1m26s\tsweet link\t\ncool tests\tpass\t1m26s\tsweet link\t\nslow tests\tpending\t1m26s\tsweet link\t\n", wantErr: "SilentError", }, { @@ -261,7 +272,7 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/somePending.json"), ) }, - wantOut: "cool tests\tpass\t1m26s\tsweet link\nrad tests\tpass\t1m26s\tsweet link\nslow tests\tpending\t1m26s\tsweet link\n", + wantOut: "cool tests\tpass\t1m26s\tsweet link\t\nrad tests\tpass\t1m26s\tsweet link\t\nslow tests\tpending\t1m26s\tsweet link\t\n", wantErr: "SilentError", }, { @@ -272,7 +283,7 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/allPassing.json"), ) }, - wantOut: "awesome tests\tpass\t1m26s\tsweet link\ncool tests\tpass\t1m26s\tsweet link\nrad tests\tpass\t1m26s\tsweet link\n", + wantOut: "awesome tests\tpass\t1m26s\tsweet link\t\ncool tests\tpass\t1m26s\tsweet link\t\nrad tests\tpass\t1m26s\tsweet link\t\n", wantErr: "", }, { @@ -283,9 +294,21 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/withStatuses.json"), ) }, - wantOut: "a status\tfail\t0\tsweet link\ncool tests\tpass\t1m26s\tsweet link\nrad tests\tpass\t1m26s\tsweet link\n", + wantOut: "a status\tfail\t0\tsweet link\t\ncool tests\tpass\t1m26s\tsweet link\t\nrad tests\tpass\t1m26s\tsweet link\t\n", wantErr: "SilentError", }, + { + name: "some skipped tty", + tty: true, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query PullRequestStatusChecks\b`), + httpmock.FileResponse("./fixtures/someSkipping.json"), + ) + }, + wantOut: "All checks were successful\n0 failing, 1 successful, 2 skipped, and 0 pending checks\n\n✓ cool tests 1m26s sweet link\n- rad tests 1m26s sweet link\n- skip tests 1m26s sweet link\n", + wantErr: "", + }, { name: "some skipped", httpStubs: func(reg *httpmock.Registry) { @@ -294,34 +317,81 @@ func Test_checksRun(t *testing.T) { httpmock.FileResponse("./fixtures/someSkipping.json"), ) }, - tty: true, - wantOut: "All checks were successful\n0 failing, 1 successful, 2 skipped, and 0 pending checks\n\n✓ cool tests 1m26s sweet link\n- rad tests 1m26s sweet link\n- skip tests 1m26s sweet link\n", + wantOut: "cool tests\tpass\t1m26s\tsweet link\t\nrad tests\tskipping\t1m26s\tsweet link\t\nskip tests\tskipping\t1m26s\tsweet link\t\n", wantErr: "", }, { - name: "only required", + name: "only required tty", + tty: true, + required: true, httpStubs: func(reg *httpmock.Registry) { reg.Register( httpmock.GraphQL(`query PullRequestStatusChecks\b`), httpmock.FileResponse("./fixtures/onlyRequired.json"), ) }, - tty: true, - wantOut: "All checks were successful\n0 failing, 1 successful, 0 skipped, and 0 pending checks\n\n✓ cool tests 1m26s sweet link\n", - wantErr: "", - required: true, + wantOut: "All checks were successful\n0 failing, 1 successful, 0 skipped, and 0 pending checks\n\n✓ cool tests 1m26s sweet link\n", + wantErr: "", }, { - name: "no required checks", + name: "only required", + required: true, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query PullRequestStatusChecks\b`), + httpmock.FileResponse("./fixtures/onlyRequired.json"), + ) + }, + wantOut: "cool tests\tpass\t1m26s\tsweet link\t\n", + wantErr: "", + }, + { + name: "only required but no required checks tty", + tty: true, + required: true, httpStubs: func(reg *httpmock.Registry) { reg.Register( httpmock.GraphQL(`query PullRequestStatusChecks\b`), httpmock.FileResponse("./fixtures/someSkipping.json"), ) }, - wantOut: "", - wantErr: "no required checks reported on the 'trunk' branch", + wantOut: "", + wantErr: "no required checks reported on the 'trunk' branch", + }, + { + name: "only required but no required checks", required: true, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query PullRequestStatusChecks\b`), + httpmock.FileResponse("./fixtures/someSkipping.json"), + ) + }, + wantOut: "", + wantErr: "no required checks reported on the 'trunk' branch", + }, + { + name: "descriptions tty", + tty: true, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query PullRequestStatusChecks\b`), + httpmock.FileResponse("./fixtures/withDescriptions.json"), + ) + }, + wantOut: "All checks were successful\n0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓ awesome tests awesome description 1m26s sweet link\n✓ cool tests cool description 1m26s sweet link\n✓ rad tests rad description 1m26s sweet link\n", + wantErr: "", + }, + { + name: "descriptions", + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query PullRequestStatusChecks\b`), + httpmock.FileResponse("./fixtures/withDescriptions.json"), + ) + }, + wantOut: "awesome tests\tpass\t1m26s\tsweet link\tawesome description\ncool tests\tpass\t1m26s\tsweet link\tcool description\nrad tests\tpass\t1m26s\tsweet link\trad description\n", + wantErr: "", }, } diff --git a/pkg/cmd/pr/checks/fixtures/withDescriptions.json b/pkg/cmd/pr/checks/fixtures/withDescriptions.json new file mode 100644 index 000000000..fd5bc9463 --- /dev/null +++ b/pkg/cmd/pr/checks/fixtures/withDescriptions.json @@ -0,0 +1,48 @@ +{ + "data": { + "node": { + "statusCheckRollup": { + "nodes": [ + { + "commit": { + "oid": "abc", + "statusCheckRollup": { + "contexts": { + "nodes": [ + { + "conclusion": "SUCCESS", + "status": "COMPLETED", + "name": "cool tests", + "description": "cool description", + "completedAt": "2020-08-27T19:00:12Z", + "startedAt": "2020-08-27T18:58:46Z", + "detailsUrl": "sweet link" + }, + { + "conclusion": "SUCCESS", + "status": "COMPLETED", + "name": "rad tests", + "description": "rad description", + "completedAt": "2020-08-27T19:00:12Z", + "startedAt": "2020-08-27T18:58:46Z", + "detailsUrl": "sweet link" + }, + { + "conclusion": "SUCCESS", + "status": "COMPLETED", + "name": "awesome tests", + "description": "awesome description", + "completedAt": "2020-08-27T19:00:12Z", + "startedAt": "2020-08-27T18:58:46Z", + "detailsUrl": "sweet link" + } + ] + } + } + } + } + ] + } + } + } +} diff --git a/pkg/cmd/pr/checks/output.go b/pkg/cmd/pr/checks/output.go index 524d3a4f5..a1e3f889a 100644 --- a/pkg/cmd/pr/checks/output.go +++ b/pkg/cmd/pr/checks/output.go @@ -4,11 +4,11 @@ import ( "fmt" "sort" + "github.com/cli/cli/v2/internal/tableprinter" "github.com/cli/cli/v2/pkg/iostreams" - "github.com/cli/cli/v2/utils" ) -func addRow(tp utils.TablePrinter, io *iostreams.IOStreams, o check) { +func addRow(tp *tableprinter.TablePrinter, io *iostreams.IOStreams, o check) { cs := io.ColorScheme() elapsed := "" @@ -42,19 +42,21 @@ func addRow(tp utils.TablePrinter, io *iostreams.IOStreams, o check) { if o.Event != "" { name += fmt.Sprintf(" (%s)", o.Event) } - tp.AddField(mark, nil, markColor) - tp.AddField(name, nil, nil) - tp.AddField(elapsed, nil, nil) - tp.AddField(o.Link, nil, nil) + tp.AddField(mark, tableprinter.WithColor(markColor)) + tp.AddField(name) + tp.AddField(o.Description) + tp.AddField(elapsed) + tp.AddField(o.Link) } else { - tp.AddField(o.Name, nil, nil) - tp.AddField(o.Bucket, nil, nil) + tp.AddField(o.Name) + tp.AddField(o.Bucket) if elapsed == "" { - tp.AddField("0", nil, nil) + tp.AddField("0") } else { - tp.AddField(elapsed, nil, nil) + tp.AddField(elapsed) } - tp.AddField(o.Link, nil, nil) + tp.AddField(o.Link) + tp.AddField(o.Description) } tp.EndRow() @@ -84,8 +86,7 @@ func printSummary(io *iostreams.IOStreams, counts checkCounts) { } func printTable(io *iostreams.IOStreams, checks []check) error { - //nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter - tp := utils.NewTablePrinter(io) + tp := tableprinter.New(io) sort.Slice(checks, func(i, j int) bool { b0 := checks[i].Bucket