Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 146 additions & 9 deletions command/pr_review.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import (
"strconv"
"strings"

"github.com/cli/cli/api"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"

"github.com/cli/cli/api"
"github.com/cli/cli/pkg/surveyext"
"github.com/cli/cli/utils"
)

func init() {
// TODO re-register post release
// prCmd.AddCommand(prReviewCmd)
prCmd.AddCommand(prReviewCmd)

prReviewCmd.Flags().BoolP("approve", "a", false, "Approve pull request")
prReviewCmd.Flags().BoolP("request-changes", "r", false, "Request changes on a pull request")
Expand All @@ -28,6 +31,8 @@ var prReviewCmd = &cobra.Command{

Examples:

gh pr review # add a review for the current branch's pull request
gh pr review 123 # add a review for pull request 123
gh pr review -a # mark the current branch's pull request as approved
gh pr review -c -b "interesting" # comment on the current branch's pull request
gh pr review 123 -r -b "needs more ascii art" # request changes on pull request 123
Expand Down Expand Up @@ -56,15 +61,19 @@ func processReviewOpt(cmd *cobra.Command) (*api.PullRequestReviewInput, error) {
state = api.ReviewComment
}

if found != 1 {
return nil, errors.New("need exactly one of --approve, --request-changes, or --comment")
}

body, err := cmd.Flags().GetString("body")
if err != nil {
return nil, err
}

if found == 0 && body == "" {
return nil, nil // signal interactive mode
} else if found == 0 && body != "" {
return nil, errors.New("--body unsupported without --approve, --request-changes, or --comment")
} else if found > 1 {
return nil, errors.New("need exactly one of --approve, --request-changes, or --comment")
}

if (flag == "request-changes" || flag == "comment") && body == "" {
return nil, fmt.Errorf("body cannot be blank for %s review", flag)
}
Expand Down Expand Up @@ -108,7 +117,7 @@ func prReview(cmd *cobra.Command, args []string) error {
}
}

input, err := processReviewOpt(cmd)
reviewData, err := processReviewOpt(cmd)
if err != nil {
return fmt.Errorf("did not understand desired review action: %w", err)
}
Expand All @@ -124,12 +133,140 @@ func prReview(cmd *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("could not find pull request: %w", err)
}
prNum = pr.Number
}

out := colorableOut(cmd)

if reviewData == nil {
reviewData, err = reviewSurvey(cmd)
if err != nil {
return err
}
if reviewData == nil && err == nil {
fmt.Fprint(out, "Discarding.\n")
return nil
}
}

err = api.AddReview(apiClient, pr, input)
err = api.AddReview(apiClient, pr, reviewData)
if err != nil {
return fmt.Errorf("failed to create review: %w", err)
}

switch reviewData.State {
case api.ReviewComment:
fmt.Fprintf(out, "%s Reviewed pull request #%d\n", utils.Gray("-"), prNum)
case api.ReviewApprove:
fmt.Fprintf(out, "%s Approved pull request #%d\n", utils.Green("✓"), prNum)
case api.ReviewRequestChanges:
fmt.Fprintf(out, "%s Requested changes to pull request #%d\n", utils.Red("+"), prNum)
}

return nil
}

func reviewSurvey(cmd *cobra.Command) (*api.PullRequestReviewInput, error) {
editorCommand, err := determineEditor(cmd)
if err != nil {
return nil, err
}

typeAnswers := struct {
ReviewType string
}{}
typeQs := []*survey.Question{
{
Name: "reviewType",
Prompt: &survey.Select{
Message: "What kind of review do you want to give?",
Options: []string{
"Comment",
"Approve",
"Request changes",
},
},
},
}

err = SurveyAsk(typeQs, &typeAnswers)
if err != nil {
return nil, err
}

reviewState := api.ReviewComment

switch typeAnswers.ReviewType {
case "Approve":
reviewState = api.ReviewApprove
case "Request Changes":
reviewState = api.ReviewRequestChanges
}

bodyAnswers := struct {
Body string
}{}

blankAllowed := false
if reviewState == api.ReviewApprove {
blankAllowed = true
}

bodyQs := []*survey.Question{
&survey.Question{
Name: "body",
Prompt: &surveyext.GhEditor{
BlankAllowed: blankAllowed,
EditorCommand: editorCommand,
Editor: &survey.Editor{
Message: "Review body",
FileName: "*.md",
},
},
},
}

err = SurveyAsk(bodyQs, &bodyAnswers)
if err != nil {
return nil, err
}

if bodyAnswers.Body == "" && (reviewState == api.ReviewComment || reviewState == api.ReviewRequestChanges) {
return nil, errors.New("this type of review cannot be blank")
}

if len(bodyAnswers.Body) > 0 {
out := colorableOut(cmd)
renderedBody, err := utils.RenderMarkdown(bodyAnswers.Body)
if err != nil {
return nil, err
}

fmt.Fprintf(out, "Got:\n%s", renderedBody)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was printing the preview of the review body intended in the final product? We don't do that in issue/pr create, so doing it here looks odd. Also, the user has just seen this text because they've authored it in their text editor, so what would be the intended purpose of printing it out right after?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we've discussed doing it for pr/issue as a review step. I thought this was a good opportunity to try it out.

}

confirm := false
confirmQs := []*survey.Question{
{
Name: "confirm",
Prompt: &survey.Confirm{
Message: "Submit?",
Default: true,
},
},
}

err = SurveyAsk(confirmQs, &confirm)
if err != nil {
return nil, err
}

if !confirm {
return nil, nil
}

return &api.PullRequestReviewInput{
Body: bodyAnswers.Body,
State: reviewState,
}, nil
}
Loading