Skip to content

Commit 2621b85

Browse files
author
Nate Smith
authored
Merge pull request cli#876 from cli/review
non-interactive gh pr review
2 parents a7242f4 + 6538cca commit 2621b85

File tree

4 files changed

+392
-1
lines changed

4 files changed

+392
-1
lines changed

api/queries_pr.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,24 @@ import (
66
"strings"
77
"time"
88

9-
"github.com/cli/cli/internal/ghrepo"
109
"github.com/shurcooL/githubv4"
10+
11+
"github.com/cli/cli/internal/ghrepo"
12+
)
13+
14+
type PullRequestReviewState int
15+
16+
const (
17+
ReviewApprove PullRequestReviewState = iota
18+
ReviewRequestChanges
19+
ReviewComment
1120
)
1221

22+
type PullRequestReviewInput struct {
23+
Body string
24+
State PullRequestReviewState
25+
}
26+
1327
type PullRequestsPayload struct {
1428
ViewerCreated PullRequestAndTotalCount
1529
ReviewRequested PullRequestAndTotalCount
@@ -456,6 +470,7 @@ func PullRequestForBranch(client *Client, repo ghrepo.Interface, baseBranch, hea
456470
repository(owner: $owner, name: $repo) {
457471
pullRequests(headRefName: $headRefName, states: OPEN, first: 30) {
458472
nodes {
473+
id
459474
number
460475
title
461476
state
@@ -593,6 +608,34 @@ func CreatePullRequest(client *Client, repo *Repository, params map[string]inter
593608
return &result.CreatePullRequest.PullRequest, nil
594609
}
595610

611+
func AddReview(client *Client, pr *PullRequest, input *PullRequestReviewInput) error {
612+
var mutation struct {
613+
AddPullRequestReview struct {
614+
ClientMutationID string
615+
} `graphql:"addPullRequestReview(input:$input)"`
616+
}
617+
618+
state := githubv4.PullRequestReviewEventComment
619+
switch input.State {
620+
case ReviewApprove:
621+
state = githubv4.PullRequestReviewEventApprove
622+
case ReviewRequestChanges:
623+
state = githubv4.PullRequestReviewEventRequestChanges
624+
}
625+
626+
body := githubv4.String(input.Body)
627+
628+
gqlInput := githubv4.AddPullRequestReviewInput{
629+
PullRequestID: pr.ID,
630+
Event: &state,
631+
Body: &body,
632+
}
633+
634+
v4 := githubv4.NewClient(client.http)
635+
636+
return v4.Mutate(context.Background(), &mutation, gqlInput, nil)
637+
}
638+
596639
func PullRequestList(client *Client, vars map[string]interface{}, limit int) (*PullRequestAndTotalCount, error) {
597640
type prBlock struct {
598641
Edges []struct {

command/pr_review.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package command
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/cli/cli/api"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
func init() {
14+
prCmd.AddCommand(prReviewCmd)
15+
16+
prReviewCmd.Flags().BoolP("approve", "a", false, "Approve pull request")
17+
prReviewCmd.Flags().BoolP("request-changes", "r", false, "Request changes on a pull request")
18+
prReviewCmd.Flags().BoolP("comment", "c", false, "Comment on a pull request")
19+
prReviewCmd.Flags().StringP("body", "b", "", "Specify the body of a review")
20+
}
21+
22+
var prReviewCmd = &cobra.Command{
23+
Use: "review [{<number> | <url> | <branch>]",
24+
Short: "Add a review to a pull request.",
25+
Args: cobra.MaximumNArgs(1),
26+
Long: `Add a review to either a specified pull request or the pull request associated with the current branch.
27+
28+
Examples:
29+
30+
gh pr review -a # mark the current branch's pull request as approved
31+
gh pr review -c -b "interesting" # comment on the current branch's pull request
32+
gh pr review 123 -r -b "needs more ascii art" # request changes on pull request 123
33+
`,
34+
RunE: prReview,
35+
}
36+
37+
func processReviewOpt(cmd *cobra.Command) (*api.PullRequestReviewInput, error) {
38+
found := 0
39+
flag := ""
40+
var state api.PullRequestReviewState
41+
42+
if cmd.Flags().Changed("approve") {
43+
found++
44+
flag = "approve"
45+
state = api.ReviewApprove
46+
}
47+
if cmd.Flags().Changed("request-changes") {
48+
found++
49+
flag = "request-changes"
50+
state = api.ReviewRequestChanges
51+
}
52+
if cmd.Flags().Changed("comment") {
53+
found++
54+
flag = "comment"
55+
state = api.ReviewComment
56+
}
57+
58+
if found != 1 {
59+
return nil, errors.New("need exactly one of --approve, --request-changes, or --comment")
60+
}
61+
62+
body, err := cmd.Flags().GetString("body")
63+
if err != nil {
64+
return nil, err
65+
}
66+
67+
if (flag == "request-changes" || flag == "comment") && body == "" {
68+
return nil, fmt.Errorf("body cannot be blank for %s review", flag)
69+
}
70+
71+
return &api.PullRequestReviewInput{
72+
Body: body,
73+
State: state,
74+
}, nil
75+
}
76+
77+
func prReview(cmd *cobra.Command, args []string) error {
78+
ctx := contextForCommand(cmd)
79+
baseRepo, err := determineBaseRepo(cmd, ctx)
80+
if err != nil {
81+
return fmt.Errorf("could not determine base repo: %w", err)
82+
}
83+
84+
apiClient, err := apiClientForContext(ctx)
85+
if err != nil {
86+
return err
87+
}
88+
89+
var prNum int
90+
branchWithOwner := ""
91+
92+
if len(args) == 0 {
93+
prNum, branchWithOwner, err = prSelectorForCurrentBranch(ctx, baseRepo)
94+
if err != nil {
95+
return fmt.Errorf("could not query for pull request for current branch: %w", err)
96+
}
97+
} else {
98+
prArg, repo := prFromURL(args[0])
99+
if repo != nil {
100+
baseRepo = repo
101+
} else {
102+
prArg = strings.TrimPrefix(args[0], "#")
103+
}
104+
prNum, err = strconv.Atoi(prArg)
105+
if err != nil {
106+
return errors.New("could not parse pull request argument")
107+
}
108+
}
109+
110+
input, err := processReviewOpt(cmd)
111+
if err != nil {
112+
return fmt.Errorf("did not understand desired review action: %w", err)
113+
}
114+
115+
var pr *api.PullRequest
116+
if prNum > 0 {
117+
pr, err = api.PullRequestByNumber(apiClient, baseRepo, prNum)
118+
if err != nil {
119+
return fmt.Errorf("could not find pull request: %w", err)
120+
}
121+
} else {
122+
pr, err = api.PullRequestForBranch(apiClient, baseRepo, "", branchWithOwner)
123+
if err != nil {
124+
return fmt.Errorf("could not find pull request: %w", err)
125+
}
126+
}
127+
128+
err = api.AddReview(apiClient, pr, input)
129+
if err != nil {
130+
return fmt.Errorf("failed to create review: %w", err)
131+
}
132+
133+
return nil
134+
}

0 commit comments

Comments
 (0)