Merge pull request #2980 from cli/auto-merge

PR merge improvements: auto-merge, edit commit body
This commit is contained in:
Mislav Marohnić 2021-02-17 16:58:49 +01:00 committed by GitHub
commit e874236ad6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 446 additions and 121 deletions

View file

@ -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
View 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
}

View file

@ -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
}

View file

@ -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())
}

View file

@ -21,6 +21,7 @@ const (
PreviewAction
CancelAction
MetadataAction
EditCommitMessageAction
noMilestone = "(none)"
)