Merge pull request #2980 from cli/auto-merge
PR merge improvements: auto-merge, edit commit body
This commit is contained in:
commit
e874236ad6
5 changed files with 446 additions and 121 deletions
|
|
@ -140,14 +140,6 @@ type PullRequestReviewStatus struct {
|
|||
ReviewRequired bool
|
||||
}
|
||||
|
||||
type PullRequestMergeMethod int
|
||||
|
||||
const (
|
||||
PullRequestMergeMethodMerge PullRequestMergeMethod = iota
|
||||
PullRequestMergeMethodRebase
|
||||
PullRequestMergeMethodSquash
|
||||
)
|
||||
|
||||
func (pr *PullRequest) ReviewStatus() PullRequestReviewStatus {
|
||||
var status PullRequestReviewStatus
|
||||
switch pr.ReviewDecision {
|
||||
|
|
@ -1088,47 +1080,6 @@ func PullRequestReopen(client *Client, repo ghrepo.Interface, pr *PullRequest) e
|
|||
return err
|
||||
}
|
||||
|
||||
func PullRequestMerge(client *Client, repo ghrepo.Interface, pr *PullRequest, m PullRequestMergeMethod, body *string) error {
|
||||
mergeMethod := githubv4.PullRequestMergeMethodMerge
|
||||
switch m {
|
||||
case PullRequestMergeMethodRebase:
|
||||
mergeMethod = githubv4.PullRequestMergeMethodRebase
|
||||
case PullRequestMergeMethodSquash:
|
||||
mergeMethod = githubv4.PullRequestMergeMethodSquash
|
||||
}
|
||||
|
||||
var mutation struct {
|
||||
MergePullRequest struct {
|
||||
PullRequest struct {
|
||||
ID githubv4.ID
|
||||
}
|
||||
} `graphql:"mergePullRequest(input: $input)"`
|
||||
}
|
||||
|
||||
input := githubv4.MergePullRequestInput{
|
||||
PullRequestID: pr.ID,
|
||||
MergeMethod: &mergeMethod,
|
||||
}
|
||||
|
||||
if m == PullRequestMergeMethodSquash {
|
||||
commitHeadline := githubv4.String(fmt.Sprintf("%s (#%d)", pr.Title, pr.Number))
|
||||
input.CommitHeadline = &commitHeadline
|
||||
}
|
||||
if body != nil {
|
||||
commitBody := githubv4.String(*body)
|
||||
input.CommitBody = &commitBody
|
||||
}
|
||||
|
||||
variables := map[string]interface{}{
|
||||
"input": input,
|
||||
}
|
||||
|
||||
gql := graphQLClient(client.http, repo.RepoHost())
|
||||
err := gql.MutateNamed(context.Background(), "PullRequestMerge", &mutation, variables)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func PullRequestReady(client *Client, repo ghrepo.Interface, pr *PullRequest) error {
|
||||
var mutation struct {
|
||||
MarkPullRequestReadyForReview struct {
|
||||
|
|
|
|||
138
pkg/cmd/pr/merge/http.go
Normal file
138
pkg/cmd/pr/merge/http.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package merge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/cli/cli/internal/ghinstance"
|
||||
"github.com/cli/cli/internal/ghrepo"
|
||||
"github.com/shurcooL/githubv4"
|
||||
"github.com/shurcooL/graphql"
|
||||
)
|
||||
|
||||
type PullRequestMergeMethod int
|
||||
|
||||
const (
|
||||
PullRequestMergeMethodMerge PullRequestMergeMethod = iota
|
||||
PullRequestMergeMethodRebase
|
||||
PullRequestMergeMethodSquash
|
||||
)
|
||||
|
||||
type mergePayload struct {
|
||||
repo ghrepo.Interface
|
||||
pullRequestID string
|
||||
method PullRequestMergeMethod
|
||||
auto bool
|
||||
commitSubject string
|
||||
setCommitSubject bool
|
||||
commitBody string
|
||||
setCommitBody bool
|
||||
}
|
||||
|
||||
// TODO: drop after githubv4 gets updated
|
||||
type EnablePullRequestAutoMergeInput struct {
|
||||
githubv4.MergePullRequestInput
|
||||
}
|
||||
|
||||
func mergePullRequest(client *http.Client, payload mergePayload) error {
|
||||
input := githubv4.MergePullRequestInput{
|
||||
PullRequestID: githubv4.ID(payload.pullRequestID),
|
||||
}
|
||||
|
||||
switch payload.method {
|
||||
case PullRequestMergeMethodMerge:
|
||||
m := githubv4.PullRequestMergeMethodMerge
|
||||
input.MergeMethod = &m
|
||||
case PullRequestMergeMethodRebase:
|
||||
m := githubv4.PullRequestMergeMethodRebase
|
||||
input.MergeMethod = &m
|
||||
case PullRequestMergeMethodSquash:
|
||||
m := githubv4.PullRequestMergeMethodSquash
|
||||
input.MergeMethod = &m
|
||||
}
|
||||
|
||||
if payload.setCommitSubject {
|
||||
commitHeadline := githubv4.String(payload.commitSubject)
|
||||
input.CommitHeadline = &commitHeadline
|
||||
}
|
||||
if payload.setCommitBody {
|
||||
commitBody := githubv4.String(payload.commitBody)
|
||||
input.CommitBody = &commitBody
|
||||
}
|
||||
|
||||
variables := map[string]interface{}{
|
||||
"input": input,
|
||||
}
|
||||
|
||||
gql := graphql.NewClient(ghinstance.GraphQLEndpoint(payload.repo.RepoHost()), client)
|
||||
|
||||
if payload.auto {
|
||||
var mutation struct {
|
||||
EnablePullRequestAutoMerge struct {
|
||||
ClientMutationId string
|
||||
} `graphql:"enablePullRequestAutoMerge(input: $input)"`
|
||||
}
|
||||
variables["input"] = EnablePullRequestAutoMergeInput{input}
|
||||
return gql.MutateNamed(context.Background(), "PullRequestAutoMerge", &mutation, variables)
|
||||
}
|
||||
|
||||
var mutation struct {
|
||||
MergePullRequest struct {
|
||||
ClientMutationId string
|
||||
} `graphql:"mergePullRequest(input: $input)"`
|
||||
}
|
||||
return gql.MutateNamed(context.Background(), "PullRequestMerge", &mutation, variables)
|
||||
}
|
||||
|
||||
func disableAutoMerge(client *http.Client, repo ghrepo.Interface, prID string) error {
|
||||
var mutation struct {
|
||||
DisablePullRequestAutoMerge struct {
|
||||
ClientMutationId string
|
||||
} `graphql:"disablePullRequestAutoMerge(input: {pullRequestId: $prID})"`
|
||||
}
|
||||
|
||||
variables := map[string]interface{}{
|
||||
"prID": githubv4.ID(prID),
|
||||
}
|
||||
|
||||
gql := graphql.NewClient(ghinstance.GraphQLEndpoint(repo.RepoHost()), client)
|
||||
return gql.MutateNamed(context.Background(), "PullRequestAutoMergeDisable", &mutation, variables)
|
||||
}
|
||||
|
||||
func getMergeText(client *http.Client, repo ghrepo.Interface, prID string, mergeMethod PullRequestMergeMethod) (string, error) {
|
||||
var method githubv4.PullRequestMergeMethod
|
||||
switch mergeMethod {
|
||||
case PullRequestMergeMethodMerge:
|
||||
method = githubv4.PullRequestMergeMethodMerge
|
||||
case PullRequestMergeMethodRebase:
|
||||
method = githubv4.PullRequestMergeMethodRebase
|
||||
case PullRequestMergeMethodSquash:
|
||||
method = githubv4.PullRequestMergeMethodSquash
|
||||
}
|
||||
|
||||
var query struct {
|
||||
Node struct {
|
||||
PullRequest struct {
|
||||
ViewerMergeBodyText string `graphql:"viewerMergeBodyText(mergeType: $method)"`
|
||||
} `graphql:"...on PullRequest"`
|
||||
} `graphql:"node(id: $prID)"`
|
||||
}
|
||||
|
||||
variables := map[string]interface{}{
|
||||
"prID": githubv4.ID(prID),
|
||||
"method": method,
|
||||
}
|
||||
|
||||
gql := graphql.NewClient(ghinstance.GraphQLEndpoint(repo.RepoHost()), client)
|
||||
err := gql.QueryNamed(context.Background(), "PullRequestMergeText", &query, variables)
|
||||
if err != nil {
|
||||
// Tolerate this API missing in older GitHub Enterprise
|
||||
if strings.Contains(err.Error(), "Field 'viewerMergeBodyText' doesn't exist") {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
return query.Node.PullRequest.ViewerMergeBodyText, nil
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/cli/cli/pkg/cmdutil"
|
||||
"github.com/cli/cli/pkg/iostreams"
|
||||
"github.com/cli/cli/pkg/prompt"
|
||||
"github.com/cli/cli/pkg/surveyext"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -29,9 +30,13 @@ type MergeOptions struct {
|
|||
|
||||
SelectorArg string
|
||||
DeleteBranch bool
|
||||
MergeMethod api.PullRequestMergeMethod
|
||||
MergeMethod PullRequestMergeMethod
|
||||
|
||||
Body *string
|
||||
AutoMergeEnable bool
|
||||
AutoMergeDisable bool
|
||||
|
||||
Body string
|
||||
BodySet bool
|
||||
|
||||
IsDeleteBranchIndicated bool
|
||||
CanDeleteLocalBranch bool
|
||||
|
|
@ -74,15 +79,15 @@ func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Comm
|
|||
|
||||
methodFlags := 0
|
||||
if flagMerge {
|
||||
opts.MergeMethod = api.PullRequestMergeMethodMerge
|
||||
opts.MergeMethod = PullRequestMergeMethodMerge
|
||||
methodFlags++
|
||||
}
|
||||
if flagRebase {
|
||||
opts.MergeMethod = api.PullRequestMergeMethodRebase
|
||||
opts.MergeMethod = PullRequestMergeMethodRebase
|
||||
methodFlags++
|
||||
}
|
||||
if flagSquash {
|
||||
opts.MergeMethod = api.PullRequestMergeMethodSquash
|
||||
opts.MergeMethod = PullRequestMergeMethodSquash
|
||||
methodFlags++
|
||||
}
|
||||
if methodFlags == 0 {
|
||||
|
|
@ -96,11 +101,7 @@ func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Comm
|
|||
|
||||
opts.IsDeleteBranchIndicated = cmd.Flags().Changed("delete-branch")
|
||||
opts.CanDeleteLocalBranch = !cmd.Flags().Changed("repo")
|
||||
|
||||
if cmd.Flags().Changed("body") {
|
||||
bodyStr, _ := cmd.Flags().GetString("body")
|
||||
opts.Body = &bodyStr
|
||||
}
|
||||
opts.BodySet = cmd.Flags().Changed("body")
|
||||
|
||||
if runF != nil {
|
||||
return runF(opts)
|
||||
|
|
@ -110,10 +111,12 @@ func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Comm
|
|||
}
|
||||
|
||||
cmd.Flags().BoolVarP(&opts.DeleteBranch, "delete-branch", "d", false, "Delete the local and remote branch after merge")
|
||||
cmd.Flags().StringP("body", "b", "", "Body for merge commit")
|
||||
cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "Body `text` for the merge commit")
|
||||
cmd.Flags().BoolVarP(&flagMerge, "merge", "m", false, "Merge the commits with the base branch")
|
||||
cmd.Flags().BoolVarP(&flagRebase, "rebase", "r", false, "Rebase the commits onto the base branch")
|
||||
cmd.Flags().BoolVarP(&flagSquash, "squash", "s", false, "Squash the commits into one commit and merge it into the base branch")
|
||||
cmd.Flags().BoolVar(&opts.AutoMergeEnable, "auto", false, "Automatically merge only after necessary requirements are met")
|
||||
cmd.Flags().BoolVar(&opts.AutoMergeDisable, "disable-auto", false, "Disable auto-merge for this pull request")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +134,19 @@ func mergeRun(opts *MergeOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
isTerminal := opts.IO.IsStdoutTTY()
|
||||
|
||||
if opts.AutoMergeDisable {
|
||||
err := disableAutoMerge(httpClient, baseRepo, pr.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isTerminal {
|
||||
fmt.Fprintf(opts.IO.ErrOut, "%s Auto-merge disabled for pull request #%d\n", cs.SuccessIconWithColor(cs.Green), pr.Number)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if opts.SelectorArg == "" {
|
||||
localBranchLastCommit, err := git.LastCommit()
|
||||
if err == nil {
|
||||
|
|
@ -148,18 +164,24 @@ func mergeRun(opts *MergeOptions) error {
|
|||
|
||||
deleteBranch := opts.DeleteBranch
|
||||
crossRepoPR := pr.HeadRepositoryOwner.Login != baseRepo.RepoOwner()
|
||||
isTerminal := opts.IO.IsStdoutTTY()
|
||||
|
||||
isPRAlreadyMerged := pr.State == "MERGED"
|
||||
if !isPRAlreadyMerged {
|
||||
mergeMethod := opts.MergeMethod
|
||||
payload := mergePayload{
|
||||
repo: baseRepo,
|
||||
pullRequestID: pr.ID,
|
||||
method: opts.MergeMethod,
|
||||
auto: opts.AutoMergeEnable,
|
||||
commitBody: opts.Body,
|
||||
setCommitBody: opts.BodySet,
|
||||
}
|
||||
|
||||
if opts.InteractiveMode {
|
||||
r, err := api.GitHubRepo(apiClient, baseRepo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mergeMethod, err = mergeMethodSurvey(r)
|
||||
payload.method, err = mergeMethodSurvey(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -167,32 +189,72 @@ func mergeRun(opts *MergeOptions) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
confirm, err := confirmSurvey()
|
||||
|
||||
allowEditMsg := payload.method != PullRequestMergeMethodRebase
|
||||
|
||||
action, err := confirmSurvey(allowEditMsg)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to confirm: %w", err)
|
||||
}
|
||||
if !confirm {
|
||||
|
||||
if action == shared.EditCommitMessageAction {
|
||||
var editorCommand string
|
||||
editorCommand, err = cmdutil.DetermineEditor(opts.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !payload.setCommitBody {
|
||||
payload.commitBody, err = getMergeText(httpClient, baseRepo, pr.ID, payload.method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
payload.commitBody, err = commitMsgSurvey(payload.commitBody, editorCommand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload.setCommitBody = true
|
||||
|
||||
action, err = confirmSurvey(false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to confirm: %w", err)
|
||||
}
|
||||
}
|
||||
if action == shared.CancelAction {
|
||||
fmt.Fprintln(opts.IO.ErrOut, "Cancelled.")
|
||||
return cmdutil.SilentError
|
||||
}
|
||||
}
|
||||
|
||||
err = api.PullRequestMerge(apiClient, baseRepo, pr, mergeMethod, opts.Body)
|
||||
err = mergePullRequest(httpClient, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isTerminal {
|
||||
action := "Merged"
|
||||
switch mergeMethod {
|
||||
case api.PullRequestMergeMethodRebase:
|
||||
action = "Rebased and merged"
|
||||
case api.PullRequestMergeMethodSquash:
|
||||
action = "Squashed and merged"
|
||||
if payload.auto {
|
||||
method := ""
|
||||
switch payload.method {
|
||||
case PullRequestMergeMethodRebase:
|
||||
method = " via rebase"
|
||||
case PullRequestMergeMethodSquash:
|
||||
method = " via squash"
|
||||
}
|
||||
fmt.Fprintf(opts.IO.ErrOut, "%s Pull request #%d will be automatically merged%s when all requirements are met\n", cs.SuccessIconWithColor(cs.Green), pr.Number, method)
|
||||
} else {
|
||||
action := "Merged"
|
||||
switch payload.method {
|
||||
case PullRequestMergeMethodRebase:
|
||||
action = "Rebased and merged"
|
||||
case PullRequestMergeMethodSquash:
|
||||
action = "Squashed and merged"
|
||||
}
|
||||
fmt.Fprintf(opts.IO.ErrOut, "%s %s pull request #%d (%s)\n", cs.SuccessIconWithColor(cs.Magenta), action, pr.Number, pr.Title)
|
||||
}
|
||||
fmt.Fprintf(opts.IO.ErrOut, "%s %s pull request #%d (%s)\n", cs.SuccessIconWithColor(cs.Magenta), action, pr.Number, pr.Title)
|
||||
}
|
||||
} else if !opts.IsDeleteBranchIndicated && opts.InteractiveMode && !crossRepoPR {
|
||||
} else if !opts.IsDeleteBranchIndicated && opts.InteractiveMode && !crossRepoPR && !opts.AutoMergeEnable {
|
||||
err := prompt.SurveyAskOne(&survey.Confirm{
|
||||
Message: fmt.Sprintf("Pull request #%d was already merged. Delete the branch locally?", pr.Number),
|
||||
Default: false,
|
||||
|
|
@ -204,7 +266,7 @@ func mergeRun(opts *MergeOptions) error {
|
|||
fmt.Fprintf(opts.IO.ErrOut, "%s Pull request #%d was already merged\n", cs.WarningIcon(), pr.Number)
|
||||
}
|
||||
|
||||
if !deleteBranch || crossRepoPR {
|
||||
if !deleteBranch || crossRepoPR || opts.AutoMergeEnable {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -259,23 +321,23 @@ func mergeRun(opts *MergeOptions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func mergeMethodSurvey(baseRepo *api.Repository) (api.PullRequestMergeMethod, error) {
|
||||
func mergeMethodSurvey(baseRepo *api.Repository) (PullRequestMergeMethod, error) {
|
||||
type mergeOption struct {
|
||||
title string
|
||||
method api.PullRequestMergeMethod
|
||||
method PullRequestMergeMethod
|
||||
}
|
||||
|
||||
var mergeOpts []mergeOption
|
||||
if baseRepo.MergeCommitAllowed {
|
||||
opt := mergeOption{title: "Create a merge commit", method: api.PullRequestMergeMethodMerge}
|
||||
opt := mergeOption{title: "Create a merge commit", method: PullRequestMergeMethodMerge}
|
||||
mergeOpts = append(mergeOpts, opt)
|
||||
}
|
||||
if baseRepo.RebaseMergeAllowed {
|
||||
opt := mergeOption{title: "Rebase and merge", method: api.PullRequestMergeMethodRebase}
|
||||
opt := mergeOption{title: "Rebase and merge", method: PullRequestMergeMethodRebase}
|
||||
mergeOpts = append(mergeOpts, opt)
|
||||
}
|
||||
if baseRepo.SquashMergeAllowed {
|
||||
opt := mergeOption{title: "Squash and merge", method: api.PullRequestMergeMethodSquash}
|
||||
opt := mergeOption{title: "Squash and merge", method: PullRequestMergeMethodSquash}
|
||||
mergeOpts = append(mergeOpts, opt)
|
||||
}
|
||||
|
||||
|
|
@ -287,7 +349,6 @@ func mergeMethodSurvey(baseRepo *api.Repository) (api.PullRequestMergeMethod, er
|
|||
mergeQuestion := &survey.Select{
|
||||
Message: "What merge method would you like to use?",
|
||||
Options: surveyOpts,
|
||||
Default: "Create a merge commit",
|
||||
}
|
||||
|
||||
var result int
|
||||
|
|
@ -316,12 +377,50 @@ func deleteBranchSurvey(opts *MergeOptions, crossRepoPR bool) (bool, error) {
|
|||
return opts.DeleteBranch, nil
|
||||
}
|
||||
|
||||
func confirmSurvey() (bool, error) {
|
||||
var confirm bool
|
||||
submit := &survey.Confirm{
|
||||
Message: "Submit?",
|
||||
Default: true,
|
||||
func confirmSurvey(allowEditMsg bool) (shared.Action, error) {
|
||||
const (
|
||||
submitLabel = "Submit"
|
||||
editCommitMsgLabel = "Edit commit message"
|
||||
cancelLabel = "Cancel"
|
||||
)
|
||||
|
||||
options := []string{submitLabel}
|
||||
if allowEditMsg {
|
||||
options = append(options, editCommitMsgLabel)
|
||||
}
|
||||
options = append(options, cancelLabel)
|
||||
|
||||
var result string
|
||||
submit := &survey.Select{
|
||||
Message: "What's next?",
|
||||
Options: options,
|
||||
}
|
||||
err := prompt.SurveyAskOne(submit, &result)
|
||||
if err != nil {
|
||||
return shared.CancelAction, fmt.Errorf("could not prompt: %w", err)
|
||||
}
|
||||
|
||||
switch result {
|
||||
case submitLabel:
|
||||
return shared.SubmitAction, nil
|
||||
case editCommitMsgLabel:
|
||||
return shared.EditCommitMessageAction, nil
|
||||
default:
|
||||
return shared.CancelAction, nil
|
||||
}
|
||||
err := prompt.SurveyAskOne(submit, &confirm)
|
||||
return confirm, err
|
||||
}
|
||||
|
||||
func commitMsgSurvey(msg string, editorCommand string) (string, error) {
|
||||
var result string
|
||||
q := &surveyext.GhEditor{
|
||||
EditorCommand: editorCommand,
|
||||
Editor: &survey.Editor{
|
||||
Message: "Body",
|
||||
AppendDefault: true,
|
||||
Default: msg,
|
||||
FileName: "*.md",
|
||||
},
|
||||
}
|
||||
err := prompt.SurveyAskOne(q, &result)
|
||||
return result, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,12 +27,11 @@ import (
|
|||
|
||||
func Test_NewCmdMerge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
isTTY bool
|
||||
want MergeOptions
|
||||
wantBody string
|
||||
wantErr string
|
||||
name string
|
||||
args string
|
||||
isTTY bool
|
||||
want MergeOptions
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "number argument",
|
||||
|
|
@ -43,8 +42,10 @@ func Test_NewCmdMerge(t *testing.T) {
|
|||
DeleteBranch: false,
|
||||
IsDeleteBranchIndicated: false,
|
||||
CanDeleteLocalBranch: true,
|
||||
MergeMethod: api.PullRequestMergeMethodMerge,
|
||||
MergeMethod: PullRequestMergeMethodMerge,
|
||||
InteractiveMode: true,
|
||||
Body: "",
|
||||
BodySet: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -56,8 +57,10 @@ func Test_NewCmdMerge(t *testing.T) {
|
|||
DeleteBranch: false,
|
||||
IsDeleteBranchIndicated: true,
|
||||
CanDeleteLocalBranch: true,
|
||||
MergeMethod: api.PullRequestMergeMethodMerge,
|
||||
MergeMethod: PullRequestMergeMethodMerge,
|
||||
InteractiveMode: true,
|
||||
Body: "",
|
||||
BodySet: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -69,10 +72,11 @@ func Test_NewCmdMerge(t *testing.T) {
|
|||
DeleteBranch: false,
|
||||
IsDeleteBranchIndicated: false,
|
||||
CanDeleteLocalBranch: true,
|
||||
MergeMethod: api.PullRequestMergeMethodMerge,
|
||||
MergeMethod: PullRequestMergeMethodMerge,
|
||||
InteractiveMode: true,
|
||||
Body: "cool",
|
||||
BodySet: true,
|
||||
},
|
||||
wantBody: "cool",
|
||||
},
|
||||
{
|
||||
name: "no argument with --repo override",
|
||||
|
|
@ -138,12 +142,8 @@ func Test_NewCmdMerge(t *testing.T) {
|
|||
assert.Equal(t, tt.want.CanDeleteLocalBranch, opts.CanDeleteLocalBranch)
|
||||
assert.Equal(t, tt.want.MergeMethod, opts.MergeMethod)
|
||||
assert.Equal(t, tt.want.InteractiveMode, opts.InteractiveMode)
|
||||
|
||||
if tt.wantBody == "" {
|
||||
assert.Nil(t, opts.Body)
|
||||
} else {
|
||||
assert.Equal(t, tt.wantBody, *opts.Body)
|
||||
}
|
||||
assert.Equal(t, tt.want.Body, opts.Body)
|
||||
assert.Equal(t, tt.want.BodySet, opts.BodySet)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -506,7 +506,7 @@ func TestPrMerge_squash(t *testing.T) {
|
|||
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
|
||||
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
|
||||
assert.Equal(t, "SQUASH", input["mergeMethod"].(string))
|
||||
assert.Equal(t, "The title of the PR (#3)", input["commitHeadline"].(string))
|
||||
assert.NotContains(t, input, "commitHeadline")
|
||||
}))
|
||||
|
||||
_, cmdTeardown := run.Stub()
|
||||
|
|
@ -610,25 +610,19 @@ func TestPRMerge_interactive(t *testing.T) {
|
|||
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
|
||||
assert.NotContains(t, input, "commitHeadline")
|
||||
}))
|
||||
http.Register(
|
||||
httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"),
|
||||
httpmock.StringResponse(`{}`))
|
||||
|
||||
cs, cmdTeardown := run.Stub()
|
||||
defer cmdTeardown(t)
|
||||
|
||||
cs.Register("git -c log.ShowSignature=false log --pretty=format:%H,%s -1", 0, "")
|
||||
cs.Register(`git config --get-regexp.+branch\\\.blueberries\\\.`, 0, "")
|
||||
cs.Register(`git checkout master`, 0, "")
|
||||
cs.Register(`git rev-parse --verify refs/heads/blueberries`, 0, "")
|
||||
cs.Register(`git branch -D blueberries`, 0, "")
|
||||
|
||||
as, surveyTeardown := prompt.InitAskStubber()
|
||||
defer surveyTeardown()
|
||||
|
||||
as.StubOne(0) // Merge method survey
|
||||
as.StubOne(true) // Delete branch survey
|
||||
as.StubOne(true) // Confirm submit survey
|
||||
as.StubOne(0) // Merge method survey
|
||||
as.StubOne(false) // Delete branch survey
|
||||
as.StubOne("Submit") // Confirm submit survey
|
||||
|
||||
output, err := runCommand(http, "blueberries", true, "")
|
||||
if err != nil {
|
||||
|
|
@ -682,8 +676,8 @@ func TestPRMerge_interactiveWithDeleteBranch(t *testing.T) {
|
|||
as, surveyTeardown := prompt.InitAskStubber()
|
||||
defer surveyTeardown()
|
||||
|
||||
as.StubOne(0) // Merge method survey
|
||||
as.StubOne(true) // Confirm submit survey
|
||||
as.StubOne(0) // Merge method survey
|
||||
as.StubOne("Submit") // Confirm submit survey
|
||||
|
||||
output, err := runCommand(http, "blueberries", true, "-d")
|
||||
if err != nil {
|
||||
|
|
@ -694,6 +688,64 @@ func TestPRMerge_interactiveWithDeleteBranch(t *testing.T) {
|
|||
test.ExpectLines(t, output.Stderr(), "Merged pull request #3", "Deleted branch blueberries and switched to branch master")
|
||||
}
|
||||
|
||||
func TestPRMerge_interactiveSquashEditCommitMsg(t *testing.T) {
|
||||
http := initFakeHTTP()
|
||||
defer http.Verify(t)
|
||||
http.Register(
|
||||
httpmock.GraphQL(`query PullRequestForBranch\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "pullRequests": { "nodes": [{
|
||||
"headRefName": "blueberries",
|
||||
"headRepositoryOwner": {"login": "OWNER"},
|
||||
"id": "THE-ID",
|
||||
"number": 3,
|
||||
"title": "title"
|
||||
}] } } } }`))
|
||||
http.Register(
|
||||
httpmock.GraphQL(`query RepositoryInfo\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": {
|
||||
"mergeCommitAllowed": true,
|
||||
"rebaseMergeAllowed": true,
|
||||
"squashMergeAllowed": true
|
||||
} } }`))
|
||||
http.Register(
|
||||
httpmock.GraphQL(`query PullRequestMergeText\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "node": {
|
||||
"viewerMergeBodyText": ""
|
||||
} } }`))
|
||||
http.Register(
|
||||
httpmock.GraphQL(`mutation PullRequestMerge\b`),
|
||||
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
|
||||
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
|
||||
assert.Equal(t, "SQUASH", input["mergeMethod"].(string))
|
||||
assert.Equal(t, "cool story", input["commitBody"].(string))
|
||||
}))
|
||||
|
||||
cs, cmdTeardown := run.Stub()
|
||||
defer cmdTeardown(t)
|
||||
|
||||
cs.Register("git -c log.ShowSignature=false log --pretty=format:%H,%s -1", 0, "")
|
||||
cs.Register(`git config --get-regexp.+branch\\\.blueberries\\\.`, 0, "")
|
||||
|
||||
as, surveyTeardown := prompt.InitAskStubber()
|
||||
defer surveyTeardown()
|
||||
|
||||
as.StubOne(2) // Merge method survey
|
||||
as.StubOne(false) // Delete branch survey
|
||||
as.StubOne("Edit commit message") // Confirm submit survey
|
||||
as.StubOne("cool story") // Edit commit message survey
|
||||
as.StubOne("Submit") // Confirm submit survey
|
||||
|
||||
output, err := runCommand(http, "blueberries", true, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Got unexpected error running `pr merge` %s", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "✓ Squashed and merged pull request #3 (title)\n", output.Stderr())
|
||||
}
|
||||
|
||||
func TestPRMerge_interactiveCancelled(t *testing.T) {
|
||||
http := initFakeHTTP()
|
||||
defer http.Verify(t)
|
||||
|
|
@ -724,9 +776,9 @@ func TestPRMerge_interactiveCancelled(t *testing.T) {
|
|||
as, surveyTeardown := prompt.InitAskStubber()
|
||||
defer surveyTeardown()
|
||||
|
||||
as.StubOne(0) // Merge method survey
|
||||
as.StubOne(true) // Delete branch survey
|
||||
as.StubOne(false) // Confirm submit survey
|
||||
as.StubOne(0) // Merge method survey
|
||||
as.StubOne(true) // Delete branch survey
|
||||
as.StubOne("Cancel") // Confirm submit survey
|
||||
|
||||
output, err := runCommand(http, "blueberries", true, "")
|
||||
if !errors.Is(err, cmdutil.SilentError) {
|
||||
|
|
@ -747,5 +799,89 @@ func Test_mergeMethodSurvey(t *testing.T) {
|
|||
as.StubOne(0) // Select first option which is rebase merge
|
||||
method, err := mergeMethodSurvey(repo)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, api.PullRequestMergeMethodRebase, method)
|
||||
assert.Equal(t, PullRequestMergeMethodRebase, method)
|
||||
}
|
||||
|
||||
func TestMergeRun_autoMerge(t *testing.T) {
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
io.SetStdoutTTY(true)
|
||||
io.SetStderrTTY(true)
|
||||
|
||||
tr := initFakeHTTP()
|
||||
defer tr.Verify(t)
|
||||
|
||||
tr.Register(
|
||||
httpmock.GraphQL(`query PullRequestByNumber\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "pullRequest": {
|
||||
"id": "THE-ID",
|
||||
"number": 123,
|
||||
"title": "The title of the PR",
|
||||
"state": "OPEN",
|
||||
"headRefName": "blueberries",
|
||||
"headRepositoryOwner": {"login": "OWNER"}
|
||||
} } } }`))
|
||||
tr.Register(
|
||||
httpmock.GraphQL(`mutation PullRequestAutoMerge\b`),
|
||||
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
|
||||
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
|
||||
assert.Equal(t, "SQUASH", input["mergeMethod"].(string))
|
||||
}))
|
||||
|
||||
_, cmdTeardown := run.Stub()
|
||||
defer cmdTeardown(t)
|
||||
|
||||
err := mergeRun(&MergeOptions{
|
||||
IO: io,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{Transport: tr}, nil
|
||||
},
|
||||
SelectorArg: "https://github.com/OWNER/REPO/pull/123",
|
||||
AutoMergeEnable: true,
|
||||
MergeMethod: PullRequestMergeMethodSquash,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "", stdout.String())
|
||||
assert.Equal(t, "✓ Pull request #123 will be automatically merged via squash when all requirements are met\n", stderr.String())
|
||||
}
|
||||
|
||||
func TestMergeRun_disableAutoMerge(t *testing.T) {
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
io.SetStdoutTTY(true)
|
||||
io.SetStderrTTY(true)
|
||||
|
||||
tr := initFakeHTTP()
|
||||
defer tr.Verify(t)
|
||||
|
||||
tr.Register(
|
||||
httpmock.GraphQL(`query PullRequestByNumber\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "pullRequest": {
|
||||
"id": "THE-ID",
|
||||
"number": 123,
|
||||
"title": "The title of the PR",
|
||||
"state": "OPEN",
|
||||
"headRefName": "blueberries",
|
||||
"headRepositoryOwner": {"login": "OWNER"}
|
||||
} } } }`))
|
||||
tr.Register(
|
||||
httpmock.GraphQL(`mutation PullRequestAutoMergeDisable\b`),
|
||||
httpmock.StringResponse(`{}`))
|
||||
|
||||
_, cmdTeardown := run.Stub()
|
||||
defer cmdTeardown(t)
|
||||
|
||||
err := mergeRun(&MergeOptions{
|
||||
IO: io,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{Transport: tr}, nil
|
||||
},
|
||||
SelectorArg: "https://github.com/OWNER/REPO/pull/123",
|
||||
AutoMergeDisable: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "", stdout.String())
|
||||
assert.Equal(t, "✓ Auto-merge disabled for pull request #123\n", stderr.String())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const (
|
|||
PreviewAction
|
||||
CancelAction
|
||||
MetadataAction
|
||||
EditCommitMessageAction
|
||||
|
||||
noMilestone = "(none)"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue