add --required flag to gh pr checks (#5648)

This commit is contained in:
ffalor 2022-09-10 03:00:29 -05:00 committed by GitHub
parent 537dcb2739
commit e5b37ee661
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 552 additions and 262 deletions

View file

@ -67,19 +67,7 @@ type PullRequest struct {
Nodes []PullRequestCommit
}
StatusCheckRollup struct {
Nodes []struct {
Commit struct {
StatusCheckRollup struct {
Contexts struct {
Nodes []CheckContext
PageInfo struct {
HasNextPage bool
EndCursor string
}
}
}
}
}
Nodes []StatusCheckRollupNode
}
Assignees Assignees
@ -93,9 +81,30 @@ type PullRequest struct {
ReviewRequests ReviewRequests
}
type StatusCheckRollupNode struct {
Commit StatusCheckRollupCommit
}
type StatusCheckRollupCommit struct {
StatusCheckRollup CommitStatusCheckRollup
}
type CommitStatusCheckRollup struct {
Contexts CheckContexts
}
type CheckContexts struct {
Nodes []CheckContext
PageInfo struct {
HasNextPage bool
EndCursor string
}
}
type CheckContext struct {
TypeName string `json:"__typename"`
Name string `json:"name"`
IsRequired bool `json:"isRequired"`
CheckSuite struct {
WorkflowRun struct {
Workflow struct {

View file

@ -164,6 +164,45 @@ func StatusCheckRollupGraphQL(after string) string {
}`), afterClause)
}
func RequiredStatusCheckRollupGraphQL(prID, after string) string {
var afterClause string
if after != "" {
afterClause = ",after:" + after
}
return fmt.Sprintf(shortenQuery(`
statusCheckRollup: commits(last: 1) {
nodes {
commit {
statusCheckRollup {
contexts(first:100%[1]s) {
nodes {
__typename
...on StatusContext {
context,
state,
targetUrl,
createdAt,
isRequired(pullRequestId: %[2]s)
},
...on CheckRun {
name,
checkSuite{workflowRun{workflow{name}}},
status,
conclusion,
startedAt,
completedAt,
detailsUrl,
isRequired(pullRequestId: %[2]s)
}
},
pageInfo{hasNextPage,endCursor}
}
}
}
}
}`), afterClause, prID)
}
var IssueFields = []string{
"assignees",
"author",

View file

@ -24,7 +24,7 @@ type checkCounts struct {
Skipping int
}
func aggregateChecks(pr *api.PullRequest) ([]check, checkCounts, error) {
func aggregateChecks(pr *api.PullRequest, requiredChecks bool) ([]check, checkCounts, error) {
checks := []check{}
counts := checkCounts{}
@ -39,6 +39,10 @@ func aggregateChecks(pr *api.PullRequest) ([]check, checkCounts, error) {
checkContexts := pr.StatusCheckRollup.Nodes[0].Commit.StatusCheckRollup.Contexts.Nodes
for _, c := range eliminateDuplicates(checkContexts) {
if requiredChecks && !c.IsRequired {
continue
}
state := c.State
if state == "" {
if c.Status == "COMPLETED" {
@ -83,6 +87,10 @@ func aggregateChecks(pr *api.PullRequest) ([]check, checkCounts, error) {
checks = append(checks, item)
}
if len(checks) == 0 && requiredChecks {
return checks, counts, fmt.Errorf("no required checks reported on the '%s' branch", pr.HeadRefName)
}
return checks, counts, nil
}

View file

@ -2,6 +2,7 @@ package checks
import (
"fmt"
"net/http"
"time"
"github.com/MakeNowJust/heredoc"
@ -18,8 +19,9 @@ import (
const defaultInterval time.Duration = 10 * time.Second
type ChecksOptions struct {
IO *iostreams.IOStreams
Browser browser.Browser
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
Browser browser.Browser
Finder shared.PRFinder
@ -27,14 +29,16 @@ type ChecksOptions struct {
WebMode bool
Interval time.Duration
Watch bool
Required bool
}
func NewCmdChecks(f *cmdutil.Factory, runF func(*ChecksOptions) error) *cobra.Command {
var interval int
opts := &ChecksOptions{
IO: f.IOStreams,
Browser: f.Browser,
Interval: defaultInterval,
HttpClient: f.HttpClient,
IO: f.IOStreams,
Browser: f.Browser,
Interval: defaultInterval,
}
cmd := &cobra.Command{
@ -82,6 +86,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().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")
return cmd
}
@ -111,6 +116,22 @@ func checksRun(opts *ChecksOptions) error {
return checksRunWebMode(opts)
}
findOptions := shared.FindOptions{
Selector: opts.SelectorArg,
Fields: []string{"number", "headRefName"},
}
var pr *api.PullRequest
pr, repo, findErr := opts.Finder.Find(findOptions)
if findErr != nil {
return findErr
}
client, clientErr := opts.HttpClient()
if clientErr != nil {
return clientErr
}
if opts.Watch {
opts.IO.StartAlternateScreenBuffer()
} else {
@ -127,19 +148,14 @@ func checksRun(opts *ChecksOptions) error {
// Do not return err until we can StopAlternateScreenBuffer()
var err error
for {
findOptions := shared.FindOptions{
Selector: opts.SelectorArg,
Fields: []string{"number", "headRefName", "statusCheckRollup"},
}
var pr *api.PullRequest
pr, _, err = opts.Finder.Find(findOptions)
for {
err = populateStatusChecks(client, repo, pr)
if err != nil {
break
}
checks, counts, err = aggregateChecks(pr)
checks, counts, err = aggregateChecks(pr, opts.Required)
if err != nil {
break
}
@ -183,3 +199,63 @@ func checksRun(opts *ChecksOptions) error {
return nil
}
func populateStatusChecks(client *http.Client, repo ghrepo.Interface, pr *api.PullRequest) error {
apiClient := api.NewClientFromHTTP(client)
type response struct {
Node *api.PullRequest
}
query := fmt.Sprintf(`
query PullRequestStatusChecks($id: ID!, $endCursor: String!) {
node(id: $id) {
...on PullRequest {
%s
}
}
}`, api.RequiredStatusCheckRollupGraphQL("$id", "$endCursor"))
variables := map[string]interface{}{
"id": pr.ID,
}
statusCheckRollup := api.CheckContexts{}
endCursor := ""
for {
variables["endCursor"] = endCursor
var resp response
err := apiClient.GraphQL(repo.RepoHost(), query, variables, &resp)
if err != nil {
return err
}
if len(resp.Node.StatusCheckRollup.Nodes) == 0 {
return nil
}
result := resp.Node.StatusCheckRollup.Nodes[0].Commit.StatusCheckRollup.Contexts
statusCheckRollup.Nodes = append(
statusCheckRollup.Nodes,
result.Nodes...,
)
if !result.PageInfo.HasNextPage {
break
}
endCursor = result.PageInfo.EndCursor
}
statusCheckRollup.PageInfo.HasNextPage = false
pr.StatusCheckRollup.Nodes = []api.StatusCheckRollupNode{{
Commit: api.StatusCheckRollupCommit{
StatusCheckRollup: api.CommitStatusCheckRollup{
Contexts: statusCheckRollup,
},
},
}}
return nil
}

View file

@ -2,9 +2,7 @@ package checks
import (
"bytes"
"encoding/json"
"io"
"os"
"net/http"
"reflect"
"testing"
"time"
@ -15,10 +13,10 @@ import (
"github.com/cli/cli/v2/internal/run"
"github.com/cli/cli/v2/pkg/cmd/pr/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewCmdChecks(t *testing.T) {
@ -64,6 +62,14 @@ func TestNewCmdChecks(t *testing.T) {
cli: "--interval 5",
wantsError: "cannot use `--interval` flag without `--watch` flag",
},
{
name: "required flag",
cli: "--required",
wants: ChecksOptions{
Required: true,
Interval: time.Duration(10000000000),
},
},
}
for _, tt := range tests {
@ -95,107 +101,198 @@ func TestNewCmdChecks(t *testing.T) {
assert.Equal(t, tt.wants.SelectorArg, gotOpts.SelectorArg)
assert.Equal(t, tt.wants.Watch, gotOpts.Watch)
assert.Equal(t, tt.wants.Interval, gotOpts.Interval)
assert.Equal(t, tt.wants.Required, gotOpts.Required)
})
}
}
func Test_checksRun(t *testing.T) {
tests := []struct {
name string
fixture string
prJSON string
tty bool
watch bool
wantOut string
wantErr string
name string
tty bool
watch bool
required bool
httpStubs func(*httpmock.Registry)
wantOut string
wantErr string
}{
{
name: "no commits",
prJSON: `{ "number": 123 }`,
tty: true,
name: "no commits",
tty: true,
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",
prJSON: `{ "number": 123, "statusCheckRollup": { "nodes": [{"commit": {"oid": "abc"}}]}, "headRefName": "master" }`,
tty: true,
name: "no checks",
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
httpmock.StringResponse(`{"data":{"node":{"statusCheckRollup":{"nodes":[{"commit":{"oid": "abc"}}]}}}}`),
)
},
wantOut: "",
wantErr: "no checks reported on the 'master' branch",
wantErr: "no checks reported on the 'trunk' branch",
},
{
name: "some failing",
fixture: "./fixtures/someFailing.json",
tty: true,
name: "some failing",
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "SilentError",
},
{
name: "some pending",
fixture: "./fixtures/somePending.json",
tty: true,
name: "some pending",
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "SilentError",
},
{
name: "all passing",
fixture: "./fixtures/allPassing.json",
tty: true,
name: "all passing",
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "",
},
{
name: "watch all passing",
fixture: "./fixtures/allPassing.json",
tty: true,
watch: true,
name: "watch all passing",
tty: true,
watch: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "",
},
{
name: "with statuses",
fixture: "./fixtures/withStatuses.json",
tty: true,
name: "with statuses",
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "SilentError",
},
{
name: "no checks",
prJSON: `{ "number": 123, "statusCheckRollup": { "nodes": [{"commit": {"oid": "abc"}}]}, "headRefName": "master" }`,
name: "no checks",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
httpmock.StringResponse(`{"data":{"node":{"statusCheckRollup":{"nodes":[{"commit":{"oid": "abc"}}]}}}}`),
)
},
wantOut: "",
wantErr: "no checks reported on the 'master' branch",
wantErr: "no checks reported on the 'trunk' branch",
},
{
name: "some failing",
fixture: "./fixtures/someFailing.json",
name: "some failing",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "SilentError",
},
{
name: "some pending",
fixture: "./fixtures/somePending.json",
name: "some pending",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "SilentError",
},
{
name: "all passing",
fixture: "./fixtures/allPassing.json",
name: "all passing",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "",
},
{
name: "with statuses",
fixture: "./fixtures/withStatuses.json",
name: "with statuses",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "SilentError",
},
{
name: "some skipped",
fixture: "./fixtures/someSkipping.json",
name: "some skipped",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
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",
wantErr: "",
},
{
name: "only required",
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,
},
{
name: "no required checks",
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",
required: true,
},
}
for _, tt := range tests {
@ -204,24 +301,22 @@ func Test_checksRun(t *testing.T) {
ios.SetStdoutTTY(tt.tty)
ios.SetAlternateScreenBufferEnabled(tt.tty)
var response *api.PullRequest
var jsonReader io.Reader
if tt.fixture != "" {
ff, err := os.Open(tt.fixture)
require.NoError(t, err)
defer ff.Close()
jsonReader = ff
} else {
jsonReader = bytes.NewBufferString(tt.prJSON)
reg := &httpmock.Registry{}
defer reg.Verify(t)
if tt.httpStubs != nil {
tt.httpStubs(reg)
}
dec := json.NewDecoder(jsonReader)
require.NoError(t, dec.Decode(&response))
response := &api.PullRequest{Number: 123, HeadRefName: "trunk"}
opts := &ChecksOptions{
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
IO: ios,
SelectorArg: "123",
Finder: shared.NewMockFinder("123", response, ghrepo.New("OWNER", "REPO")),
Watch: tt.watch,
Required: tt.required,
}
err := checksRun(opts)

View file

@ -1,42 +1,45 @@
{
"number": 123,
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "awesome tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
{
"data": {
"node": {
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "awesome tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
}
]
}
]
}
}
}
}
]
}
]
}
}
}

View file

@ -0,0 +1,48 @@
{
"data": {
"node": {
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link",
"isRequired": true
},
{
"conclusion": "SKIPPED",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link",
"isRequired": false
},
{
"conclusion": "SKIPPED",
"status": "COMPLETED",
"name": "skip tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link",
"isRequired": false
}
]
}
}
}
}
]
}
}
}
}

View file

@ -1,42 +1,45 @@
{
"number": 123,
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "FAILURE",
"status": "COMPLETED",
"name": "sad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "",
"status": "IN_PROGRESS",
"name": "slow tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
{
"data": {
"node": {
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "FAILURE",
"status": "COMPLETED",
"name": "sad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "",
"status": "IN_PROGRESS",
"name": "slow tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
}
]
}
]
}
}
}
}
]
}
]
}
}
}

View file

@ -1,42 +1,45 @@
{
"number": 123,
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "",
"status": "IN_PROGRESS",
"name": "slow tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
{
"data": {
"node": {
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "",
"status": "IN_PROGRESS",
"name": "slow tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
}
]
}
]
}
}
}
}
]
}
]
}
}
}

View file

@ -1,42 +1,45 @@
{
"number": 123,
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SKIPPED",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SKIPPED",
"status": "COMPLETED",
"name": "skip tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
{
"data": {
"node": {
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SKIPPED",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SKIPPED",
"status": "COMPLETED",
"name": "skip tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
}
]
}
]
}
}
}
}
]
}
]
}
}
}

View file

@ -1,39 +1,42 @@
{
"number": 123,
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"state": "FAILURE",
"name": "a status",
"targetUrl": "sweet link"
{
"data": {
"node": {
"statusCheckRollup": {
"nodes": [
{
"commit": {
"oid": "abc",
"statusCheckRollup": {
"contexts": {
"nodes": [
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "cool tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"conclusion": "SUCCESS",
"status": "COMPLETED",
"name": "rad tests",
"completedAt": "2020-08-27T19:00:12Z",
"startedAt": "2020-08-27T18:58:46Z",
"detailsUrl": "sweet link"
},
{
"state": "FAILURE",
"name": "a status",
"targetUrl": "sweet link"
}
]
}
]
}
}
}
}
]
}
]
}
}
}