From 862b5228d0bb15d8163a289f6a38a02b51c821bd Mon Sep 17 00:00:00 2001 From: Brenden Matthews Date: Thu, 7 Feb 2019 14:23:31 -0500 Subject: [PATCH 1/2] [automerge] Use available merge methods. For repos with certain merge methods disabled (such as 'merge'), simply hitting the GitHub `/merge` endpoint is not sufficient. Instead, we need to check which methods are actually available, then use the appropriate method. With this change it will: 1. Default to 'merge' if enabled 2. Use 'rebase' if 'merge' is disabled and rebase is enabled 3. Use 'squash' if 'merge' is disabled --- .../github-pull-request-baserepo.json | 105 ++++++++++++++++++ server/events/vcs/github_client.go | 17 ++- server/events/vcs/github_client_test.go | 8 +- 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 server/events/vcs/fixtures/github-pull-request-baserepo.json diff --git a/server/events/vcs/fixtures/github-pull-request-baserepo.json b/server/events/vcs/fixtures/github-pull-request-baserepo.json new file mode 100644 index 0000000000..4f44926c9d --- /dev/null +++ b/server/events/vcs/fixtures/github-pull-request-baserepo.json @@ -0,0 +1,105 @@ +{ + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": true, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "http://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "topics": [ + "octocat", + "atom", + "electron", + "API" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "allow_rebase_merge": true, + "allow_squash_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0 +} diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index 15fbc1b691..61bec6c8ac 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -178,13 +178,28 @@ func (g *GithubClient) UpdateStatus(repo models.Repo, pull models.PullRequest, s // MergePull merges the pull request. func (g *GithubClient) MergePull(pull models.PullRequest) error { + // Determine which method to use for merging this PR + repo, _, err := g.client.Repositories.Get(g.ctx, pull.BaseRepo.Owner, pull.BaseRepo.Name) + if err != nil { + return errors.Wrap(err, "fetching repo info") + } + // Default method is "merge" + method := "merge" + if !*repo.AllowMergeCommit && *repo.AllowRebaseMerge { + method = "rebase" + } else if !*repo.AllowMergeCommit && *repo.AllowSquashMerge { + method = "squash" + } + options := &github.PullRequestOptions{ + MergeMethod: method, + } mergeResult, _, err := g.client.PullRequests.Merge( g.ctx, pull.BaseRepo.Owner, pull.BaseRepo.Name, pull.Num, common.AutomergeCommitMsg, - nil) + options) if err != nil { return errors.Wrap(err, "merging pull request") } diff --git a/server/events/vcs/github_client_test.go b/server/events/vcs/github_client_test.go index 86477afa44..86f050576b 100644 --- a/server/events/vcs/github_client_test.go +++ b/server/events/vcs/github_client_test.go @@ -312,15 +312,21 @@ func TestGithubClient_MergePull(t *testing.T) { }, } + // Use a real GitHub json response and edit the mergeable_state field. + jsBytes, _ := ioutil.ReadFile("fixtures/github-pull-request-baserepo.json") + for _, c := range cases { t.Run(c.message, func(t *testing.T) { testServer := httptest.NewTLSServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.RequestURI { + case "/api/v3/repos/owner/repo": + w.Write(jsBytes) // nolint: errcheck + return case "/api/v3/repos/owner/repo/pulls/1/merge": body, err := ioutil.ReadAll(r.Body) Ok(t, err) - exp := "{\"commit_message\":\"[Atlantis] Automatically merging after successful apply\"}\n" + exp := "{\"commit_message\":\"[Atlantis] Automatically merging after successful apply\",\"merge_method\":\"merge\"}\n" Equals(t, exp, string(body)) var resp string if c.code == 200 { From def41f6cefa248a47fc59b8a0a7aef6897890288 Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Mon, 11 Feb 2019 11:12:15 -0600 Subject: [PATCH 2/2] Add tests for GH merge-type detection. --- .../github-pull-request-baserepo.json | 105 --------------- server/events/vcs/fixtures/github-repo.json | 103 +++++++++++++++ server/events/vcs/github_client.go | 23 +++- server/events/vcs/github_client_test.go | 125 +++++++++++++++++- 4 files changed, 241 insertions(+), 115 deletions(-) delete mode 100644 server/events/vcs/fixtures/github-pull-request-baserepo.json create mode 100644 server/events/vcs/fixtures/github-repo.json diff --git a/server/events/vcs/fixtures/github-pull-request-baserepo.json b/server/events/vcs/fixtures/github-pull-request-baserepo.json deleted file mode 100644 index 4f44926c9d..0000000000 --- a/server/events/vcs/fixtures/github-pull-request-baserepo.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "id": 1296269, - "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", - "name": "Hello-World", - "full_name": "octocat/Hello-World", - "owner": { - "login": "octocat", - "id": 1, - "node_id": "MDQ6VXNlcjE=", - "avatar_url": "https://github.com/images/error/octocat_happy.gif", - "gravatar_id": "", - "url": "https://api.github.com/users/octocat", - "html_url": "https://github.com/octocat", - "followers_url": "https://api.github.com/users/octocat/followers", - "following_url": "https://api.github.com/users/octocat/following{/other_user}", - "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", - "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", - "organizations_url": "https://api.github.com/users/octocat/orgs", - "repos_url": "https://api.github.com/users/octocat/repos", - "events_url": "https://api.github.com/users/octocat/events{/privacy}", - "received_events_url": "https://api.github.com/users/octocat/received_events", - "type": "User", - "site_admin": false - }, - "private": false, - "html_url": "https://github.com/octocat/Hello-World", - "description": "This your first repo!", - "fork": true, - "url": "https://api.github.com/repos/octocat/Hello-World", - "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", - "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", - "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", - "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", - "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", - "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", - "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", - "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", - "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", - "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", - "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", - "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", - "events_url": "http://api.github.com/repos/octocat/Hello-World/events", - "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", - "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", - "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", - "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", - "git_url": "git:github.com/octocat/Hello-World.git", - "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", - "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", - "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", - "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", - "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", - "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", - "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", - "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", - "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", - "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", - "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", - "ssh_url": "git@github.com:octocat/Hello-World.git", - "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", - "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", - "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", - "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", - "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", - "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", - "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", - "clone_url": "https://github.com/octocat/Hello-World.git", - "mirror_url": "git:git.example.com/octocat/Hello-World", - "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", - "svn_url": "https://svn.github.com/octocat/Hello-World", - "homepage": "https://github.com", - "language": null, - "forks_count": 9, - "stargazers_count": 80, - "watchers_count": 80, - "size": 108, - "default_branch": "master", - "open_issues_count": 0, - "topics": [ - "octocat", - "atom", - "electron", - "API" - ], - "has_issues": true, - "has_projects": true, - "has_wiki": true, - "has_pages": false, - "has_downloads": true, - "archived": false, - "pushed_at": "2011-01-26T19:06:43Z", - "created_at": "2011-01-26T19:01:12Z", - "updated_at": "2011-01-26T19:14:43Z", - "permissions": { - "admin": false, - "push": false, - "pull": true - }, - "allow_rebase_merge": true, - "allow_squash_merge": true, - "allow_merge_commit": true, - "subscribers_count": 42, - "network_count": 0 -} diff --git a/server/events/vcs/fixtures/github-repo.json b/server/events/vcs/fixtures/github-repo.json new file mode 100644 index 0000000000..3012d24771 --- /dev/null +++ b/server/events/vcs/fixtures/github-repo.json @@ -0,0 +1,103 @@ +{ + "id": 167228802, + "node_id": "MDEwOlJlcG9zaXRvcnkxNjcyMjg4MDI=", + "name": "atlantis", + "full_name": "runatlantis/atlantis", + "private": false, + "owner": { + "login": "runatlantis", + "id": 1034429, + "node_id": "MDQ6VXNlcjEwMzQ0Mjk=", + "avatar_url": "https://avatars1.githubusercontent.com/u/1034429?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/runatlantis", + "html_url": "https://github.com/runatlantis", + "followers_url": "https://api.github.com/users/runatlantis/followers", + "following_url": "https://api.github.com/users/runatlantis/following{/other_user}", + "gists_url": "https://api.github.com/users/runatlantis/gists{/gist_id}", + "starred_url": "https://api.github.com/users/runatlantis/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/runatlantis/subscriptions", + "organizations_url": "https://api.github.com/users/runatlantis/orgs", + "repos_url": "https://api.github.com/users/runatlantis/repos", + "events_url": "https://api.github.com/users/runatlantis/events{/privacy}", + "received_events_url": "https://api.github.com/users/runatlantis/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/runatlantis/atlantis", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/runatlantis/atlantis", + "forks_url": "https://api.github.com/repos/runatlantis/atlantis/forks", + "keys_url": "https://api.github.com/repos/runatlantis/atlantis/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/runatlantis/atlantis/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/runatlantis/atlantis/teams", + "hooks_url": "https://api.github.com/repos/runatlantis/atlantis/hooks", + "issue_events_url": "https://api.github.com/repos/runatlantis/atlantis/issues/events{/number}", + "events_url": "https://api.github.com/repos/runatlantis/atlantis/events", + "assignees_url": "https://api.github.com/repos/runatlantis/atlantis/assignees{/user}", + "branches_url": "https://api.github.com/repos/runatlantis/atlantis/branches{/branch}", + "tags_url": "https://api.github.com/repos/runatlantis/atlantis/tags", + "blobs_url": "https://api.github.com/repos/runatlantis/atlantis/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/runatlantis/atlantis/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/runatlantis/atlantis/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/runatlantis/atlantis/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/runatlantis/atlantis/statuses/{sha}", + "languages_url": "https://api.github.com/repos/runatlantis/atlantis/languages", + "stargazers_url": "https://api.github.com/repos/runatlantis/atlantis/stargazers", + "contributors_url": "https://api.github.com/repos/runatlantis/atlantis/contributors", + "subscribers_url": "https://api.github.com/repos/runatlantis/atlantis/subscribers", + "subscription_url": "https://api.github.com/repos/runatlantis/atlantis/subscription", + "commits_url": "https://api.github.com/repos/runatlantis/atlantis/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/runatlantis/atlantis/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/runatlantis/atlantis/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/runatlantis/atlantis/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/runatlantis/atlantis/contents/{+path}", + "compare_url": "https://api.github.com/repos/runatlantis/atlantis/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/runatlantis/atlantis/merges", + "archive_url": "https://api.github.com/repos/runatlantis/atlantis/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/runatlantis/atlantis/downloads", + "issues_url": "https://api.github.com/repos/runatlantis/atlantis/issues{/number}", + "pulls_url": "https://api.github.com/repos/runatlantis/atlantis/pulls{/number}", + "milestones_url": "https://api.github.com/repos/runatlantis/atlantis/milestones{/number}", + "notifications_url": "https://api.github.com/repos/runatlantis/atlantis/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/runatlantis/atlantis/labels{/name}", + "releases_url": "https://api.github.com/repos/runatlantis/atlantis/releases{/id}", + "deployments_url": "https://api.github.com/repos/runatlantis/atlantis/deployments", + "created_at": "2019-01-23T17:58:45Z", + "updated_at": "2019-02-08T21:46:28Z", + "pushed_at": "2019-02-10T01:49:25Z", + "git_url": "git://github.com/runatlantis/atlantis.git", + "ssh_url": "git@github.com:runatlantis/atlantis.git", + "clone_url": "https://github.com/runatlantis/atlantis.git", + "svn_url": "https://github.com/runatlantis/atlantis", + "homepage": null, + "size": 32, + "stargazers_count": 0, + "watchers_count": 0, + "language": "HCL", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "open_issues_count": 1, + "license": null, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master", + "permissions": { + "admin": true, + "push": true, + "pull": true + }, + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "network_count": 0, + "subscribers_count": 0 +} diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index 61bec6c8ac..05e7f1359c 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -178,18 +178,27 @@ func (g *GithubClient) UpdateStatus(repo models.Repo, pull models.PullRequest, s // MergePull merges the pull request. func (g *GithubClient) MergePull(pull models.PullRequest) error { - // Determine which method to use for merging this PR + // Users can set their repo to disallow certain types of merging. + // We detect which types aren't allowed and use the type that is. repo, _, err := g.client.Repositories.Get(g.ctx, pull.BaseRepo.Owner, pull.BaseRepo.Name) if err != nil { return errors.Wrap(err, "fetching repo info") } - // Default method is "merge" - method := "merge" - if !*repo.AllowMergeCommit && *repo.AllowRebaseMerge { - method = "rebase" - } else if !*repo.AllowMergeCommit && *repo.AllowSquashMerge { - method = "squash" + const ( + defaultMergeMethod = "merge" + rebaseMergeMethod = "rebase" + squashMergeMethod = "squash" + ) + method := defaultMergeMethod + if !repo.GetAllowMergeCommit() { + if repo.GetAllowRebaseMerge() { + method = rebaseMergeMethod + } else if repo.GetAllowSquashMerge() { + method = squashMergeMethod + } } + + // Now we're ready to make our API call to merge the pull request. options := &github.PullRequestOptions{ MergeMethod: method, } diff --git a/server/events/vcs/github_client_test.go b/server/events/vcs/github_client_test.go index 86f050576b..9607a0c5cd 100644 --- a/server/events/vcs/github_client_test.go +++ b/server/events/vcs/github_client_test.go @@ -2,6 +2,7 @@ package vcs_test import ( "crypto/tls" + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -287,7 +288,7 @@ func TestGithubClient_PullIsMergeable(t *testing.T) { } } -func TestGithubClient_MergePull(t *testing.T) { +func TestGithubClient_MergePullHandlesError(t *testing.T) { cases := []struct { code int message string @@ -312,8 +313,8 @@ func TestGithubClient_MergePull(t *testing.T) { }, } - // Use a real GitHub json response and edit the mergeable_state field. - jsBytes, _ := ioutil.ReadFile("fixtures/github-pull-request-baserepo.json") + jsBytes, err := ioutil.ReadFile("fixtures/github-repo.json") + Ok(t, err) for _, c := range cases { t.Run(c.message, func(t *testing.T) { @@ -375,6 +376,124 @@ func TestGithubClient_MergePull(t *testing.T) { } } +// Test that if the pull request only allows a certain merge method that we +// use that method +func TestGithubClient_MergePullCorrectMethod(t *testing.T) { + cases := map[string]struct { + allowMerge bool + allowRebase bool + allowSquash bool + expMethod string + }{ + "all true": { + allowMerge: true, + allowRebase: true, + allowSquash: true, + expMethod: "merge", + }, + "all false (edge case)": { + allowMerge: false, + allowRebase: false, + allowSquash: false, + expMethod: "merge", + }, + "merge: false rebase: true squash: true": { + allowMerge: false, + allowRebase: true, + allowSquash: true, + expMethod: "rebase", + }, + "merge: false rebase: false squash: true": { + allowMerge: false, + allowRebase: false, + allowSquash: true, + expMethod: "squash", + }, + "merge: false rebase: true squash: false": { + allowMerge: false, + allowRebase: true, + allowSquash: false, + expMethod: "rebase", + }, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + + // Modify response. + jsBytes, err := ioutil.ReadFile("fixtures/github-repo.json") + Ok(t, err) + resp := string(jsBytes) + resp = strings.Replace(resp, + `"allow_squash_merge": true`, + fmt.Sprintf(`"allow_squash_merge": %t`, c.allowSquash), + -1) + resp = strings.Replace(resp, + `"allow_merge_commit": true`, + fmt.Sprintf(`"allow_merge_commit": %t`, c.allowMerge), + -1) + resp = strings.Replace(resp, + `"allow_rebase_merge": true`, + fmt.Sprintf(`"allow_rebase_merge": %t`, c.allowRebase), + -1) + + testServer := httptest.NewTLSServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.RequestURI { + case "/api/v3/repos/runatlantis/atlantis": + w.Write([]byte(resp)) // nolint: errcheck + return + case "/api/v3/repos/runatlantis/atlantis/pulls/1/merge": + body, err := ioutil.ReadAll(r.Body) + Ok(t, err) + defer r.Body.Close() // nolint: errcheck + type bodyJSON struct { + CommitMessage string `json:"commit_message"` + MergeMethod string `json:"merge_method"` + } + expBody := bodyJSON{ + CommitMessage: "[Atlantis] Automatically merging after successful apply", + MergeMethod: c.expMethod, + } + expBytes, err := json.Marshal(expBody) + Ok(t, err) + Equals(t, string(expBytes)+"\n", string(body)) + + resp := `{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e","merged":true,"message":"Pull Request successfully merged"}` + w.Write([]byte(resp)) // nolint: errcheck + default: + t.Errorf("got unexpected request at %q", r.RequestURI) + http.Error(w, "not found", http.StatusNotFound) + return + } + })) + + testServerURL, err := url.Parse(testServer.URL) + Ok(t, err) + client, err := vcs.NewGithubClient(testServerURL.Host, "user", "pass") + Ok(t, err) + defer disableSSLVerification()() + + err = client.MergePull( + models.PullRequest{ + BaseRepo: models.Repo{ + FullName: "runatlantis/atlantis", + Owner: "runatlantis", + Name: "atlantis", + CloneURL: "", + SanitizedCloneURL: "", + VCSHost: models.VCSHost{ + Type: models.Github, + Hostname: "github.com", + }, + }, + Num: 1, + }) + Ok(t, err) + }) + } +} + // disableSSLVerification disables ssl verification for the global http client // and returns a function to be called in a defer that will re-enable it. func disableSSLVerification() func() {