Skip to content

Commit f7074c1

Browse files
author
Nate Smith
authored
Merge pull request cli#55 from github/pr-status-checks
Add checks, reviews info to `pr status`
2 parents 2236bd3 + 8fea481 commit f7074c1

File tree

3 files changed

+165
-9
lines changed

3 files changed

+165
-9
lines changed

api/queries.go

Lines changed: 132 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,99 @@ type PullRequest struct {
1717
State string
1818
URL string
1919
HeadRefName string
20+
Reviews struct {
21+
Nodes []struct {
22+
State string
23+
Author struct {
24+
Login string
25+
}
26+
}
27+
}
28+
Commits struct {
29+
Nodes []struct {
30+
Commit struct {
31+
Status struct {
32+
Contexts []struct {
33+
State string
34+
}
35+
}
36+
CheckSuites struct {
37+
Nodes []struct {
38+
CheckRuns struct {
39+
Nodes []struct {
40+
Conclusion string
41+
}
42+
}
43+
}
44+
}
45+
}
46+
}
47+
}
48+
}
49+
50+
type PullRequestReviewStatus struct {
51+
ChangesRequested bool
52+
Approved bool
53+
}
54+
55+
func (pr *PullRequest) ReviewStatus() PullRequestReviewStatus {
56+
status := PullRequestReviewStatus{}
57+
reviewMap := map[string]string{}
58+
// Reviews will include every review on record, including consecutive ones
59+
// from the same actor. Consolidate them into latest state per reviewer.
60+
for _, review := range pr.Reviews.Nodes {
61+
reviewMap[review.Author.Login] = review.State
62+
}
63+
for _, state := range reviewMap {
64+
switch state {
65+
case "CHANGES_REQUESTED":
66+
status.ChangesRequested = true
67+
case "APPROVED":
68+
status.Approved = true
69+
}
70+
}
71+
return status
72+
}
73+
74+
type PullRequestChecksStatus struct {
75+
Pending int
76+
Failing int
77+
Passing int
78+
Total int
79+
}
80+
81+
func (pr *PullRequest) ChecksStatus() (summary PullRequestChecksStatus) {
82+
if len(pr.Commits.Nodes) == 0 {
83+
return
84+
}
85+
commit := pr.Commits.Nodes[0].Commit
86+
for _, status := range commit.Status.Contexts {
87+
switch status.State {
88+
case "SUCCESS":
89+
summary.Passing++
90+
case "EXPECTED", "ERROR", "FAILURE":
91+
summary.Failing++
92+
case "PENDING":
93+
summary.Pending++
94+
default:
95+
panic(fmt.Errorf("unsupported status: %q", status.State))
96+
}
97+
summary.Total++
98+
}
99+
for _, checkSuite := range commit.CheckSuites.Nodes {
100+
for _, checkRun := range checkSuite.CheckRuns.Nodes {
101+
switch checkRun.Conclusion {
102+
case "SUCCESS", "NEUTRAL":
103+
summary.Passing++
104+
case "FAILURE", "CANCELLED", "TIMED_OUT", "ACTION_REQUIRED":
105+
summary.Failing++
106+
default:
107+
panic(fmt.Errorf("unsupported check conclusion: %q", checkRun.Conclusion))
108+
}
109+
summary.Total++
110+
}
111+
}
112+
return
20113
}
21114

22115
type Repo interface {
@@ -147,27 +240,58 @@ func PullRequests(client *Client, ghRepo Repo, currentBranch, currentUsername st
147240
}
148241

149242
query := `
150-
fragment pr on PullRequest {
151-
number
152-
title
153-
url
154-
headRefName
155-
}
243+
fragment pr on PullRequest {
244+
number
245+
title
246+
url
247+
headRefName
248+
commits(last: 1) {
249+
nodes {
250+
commit {
251+
status {
252+
contexts {
253+
state
254+
}
255+
}
256+
checkSuites(first: 50) {
257+
nodes {
258+
checkRuns(first: 50) {
259+
nodes {
260+
conclusion
261+
}
262+
}
263+
}
264+
}
265+
}
266+
}
267+
}
268+
}
269+
fragment prWithReviews on PullRequest {
270+
...pr
271+
reviews(last: 20) {
272+
nodes {
273+
state
274+
author {
275+
login
276+
}
277+
}
278+
}
279+
}
156280
157281
query($owner: String!, $repo: String!, $headRefName: String!, $viewerQuery: String!, $reviewerQuery: String!, $per_page: Int = 10) {
158282
repository(owner: $owner, name: $repo) {
159283
pullRequests(headRefName: $headRefName, states: OPEN, first: 1) {
160284
edges {
161285
node {
162-
...pr
286+
...prWithReviews
163287
}
164288
}
165289
}
166290
}
167291
viewerCreated: search(query: $viewerQuery, type: ISSUE, first: $per_page) {
168292
edges {
169293
node {
170-
...pr
294+
...prWithReviews
171295
}
172296
}
173297
pageInfo {

command/pr.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,37 @@ func prView(cmd *cobra.Command, args []string) error {
248248

249249
func printPrs(prs ...api.PullRequest) {
250250
for _, pr := range prs {
251-
fmt.Printf(" #%d %s %s\n", pr.Number, truncate(50, pr.Title), utils.Cyan("["+pr.HeadRefName+"]"))
251+
prNumber := fmt.Sprintf("#%d", pr.Number)
252+
fmt.Printf(" %s %s %s", utils.Yellow(prNumber), truncate(50, pr.Title), utils.Cyan("["+pr.HeadRefName+"]"))
253+
254+
checks := pr.ChecksStatus()
255+
reviews := pr.ReviewStatus()
256+
if checks.Total > 0 || reviews.ChangesRequested || reviews.Approved {
257+
fmt.Printf("\n ")
258+
}
259+
260+
if checks.Total > 0 {
261+
var ratio string
262+
if checks.Failing > 0 {
263+
ratio = fmt.Sprintf("%d/%d", checks.Passing, checks.Total)
264+
ratio = utils.Red(ratio)
265+
} else if checks.Pending > 0 {
266+
ratio = fmt.Sprintf("%d/%d", checks.Passing, checks.Total)
267+
ratio = utils.Yellow(ratio)
268+
} else if checks.Passing == checks.Total {
269+
ratio = fmt.Sprintf("%d", checks.Total)
270+
ratio = utils.Green(ratio)
271+
}
272+
fmt.Printf(" - checks: %s", ratio)
273+
}
274+
275+
if reviews.ChangesRequested {
276+
fmt.Printf(" - %s", utils.Red("changes requested"))
277+
} else if reviews.Approved {
278+
fmt.Printf(" - %s", utils.Green("approved"))
279+
}
280+
281+
fmt.Printf("\n")
252282
}
253283
}
254284

command/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) {
7272
opts := []api.ClientOption{
7373
api.AddHeader("Authorization", fmt.Sprintf("token %s", token)),
7474
api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", Version)),
75+
// antiope-preview: Checks
76+
api.AddHeader("Accept", "application/vnd.github.antiope-preview+json"),
7577
}
7678
if verbose := os.Getenv("DEBUG"); verbose != "" {
7779
opts = append(opts, api.VerboseLog(os.Stderr))

0 commit comments

Comments
 (0)