Skip to content

Commit 45a6823

Browse files
authored
Merge pull request #466 from runatlantis/gh-automerge
GitHub Automerge Types
2 parents 1de4034 + def41f6 commit 45a6823

File tree

3 files changed

+255
-3
lines changed

3 files changed

+255
-3
lines changed
+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{
2+
"id": 167228802,
3+
"node_id": "MDEwOlJlcG9zaXRvcnkxNjcyMjg4MDI=",
4+
"name": "atlantis",
5+
"full_name": "runatlantis/atlantis",
6+
"private": false,
7+
"owner": {
8+
"login": "runatlantis",
9+
"id": 1034429,
10+
"node_id": "MDQ6VXNlcjEwMzQ0Mjk=",
11+
"avatar_url": "https://avatars1.githubusercontent.com/u/1034429?v=4",
12+
"gravatar_id": "",
13+
"url": "https://api.github.com/users/runatlantis",
14+
"html_url": "https://github.com/runatlantis",
15+
"followers_url": "https://api.github.com/users/runatlantis/followers",
16+
"following_url": "https://api.github.com/users/runatlantis/following{/other_user}",
17+
"gists_url": "https://api.github.com/users/runatlantis/gists{/gist_id}",
18+
"starred_url": "https://api.github.com/users/runatlantis/starred{/owner}{/repo}",
19+
"subscriptions_url": "https://api.github.com/users/runatlantis/subscriptions",
20+
"organizations_url": "https://api.github.com/users/runatlantis/orgs",
21+
"repos_url": "https://api.github.com/users/runatlantis/repos",
22+
"events_url": "https://api.github.com/users/runatlantis/events{/privacy}",
23+
"received_events_url": "https://api.github.com/users/runatlantis/received_events",
24+
"type": "User",
25+
"site_admin": false
26+
},
27+
"html_url": "https://github.com/runatlantis/atlantis",
28+
"description": null,
29+
"fork": false,
30+
"url": "https://api.github.com/repos/runatlantis/atlantis",
31+
"forks_url": "https://api.github.com/repos/runatlantis/atlantis/forks",
32+
"keys_url": "https://api.github.com/repos/runatlantis/atlantis/keys{/key_id}",
33+
"collaborators_url": "https://api.github.com/repos/runatlantis/atlantis/collaborators{/collaborator}",
34+
"teams_url": "https://api.github.com/repos/runatlantis/atlantis/teams",
35+
"hooks_url": "https://api.github.com/repos/runatlantis/atlantis/hooks",
36+
"issue_events_url": "https://api.github.com/repos/runatlantis/atlantis/issues/events{/number}",
37+
"events_url": "https://api.github.com/repos/runatlantis/atlantis/events",
38+
"assignees_url": "https://api.github.com/repos/runatlantis/atlantis/assignees{/user}",
39+
"branches_url": "https://api.github.com/repos/runatlantis/atlantis/branches{/branch}",
40+
"tags_url": "https://api.github.com/repos/runatlantis/atlantis/tags",
41+
"blobs_url": "https://api.github.com/repos/runatlantis/atlantis/git/blobs{/sha}",
42+
"git_tags_url": "https://api.github.com/repos/runatlantis/atlantis/git/tags{/sha}",
43+
"git_refs_url": "https://api.github.com/repos/runatlantis/atlantis/git/refs{/sha}",
44+
"trees_url": "https://api.github.com/repos/runatlantis/atlantis/git/trees{/sha}",
45+
"statuses_url": "https://api.github.com/repos/runatlantis/atlantis/statuses/{sha}",
46+
"languages_url": "https://api.github.com/repos/runatlantis/atlantis/languages",
47+
"stargazers_url": "https://api.github.com/repos/runatlantis/atlantis/stargazers",
48+
"contributors_url": "https://api.github.com/repos/runatlantis/atlantis/contributors",
49+
"subscribers_url": "https://api.github.com/repos/runatlantis/atlantis/subscribers",
50+
"subscription_url": "https://api.github.com/repos/runatlantis/atlantis/subscription",
51+
"commits_url": "https://api.github.com/repos/runatlantis/atlantis/commits{/sha}",
52+
"git_commits_url": "https://api.github.com/repos/runatlantis/atlantis/git/commits{/sha}",
53+
"comments_url": "https://api.github.com/repos/runatlantis/atlantis/comments{/number}",
54+
"issue_comment_url": "https://api.github.com/repos/runatlantis/atlantis/issues/comments{/number}",
55+
"contents_url": "https://api.github.com/repos/runatlantis/atlantis/contents/{+path}",
56+
"compare_url": "https://api.github.com/repos/runatlantis/atlantis/compare/{base}...{head}",
57+
"merges_url": "https://api.github.com/repos/runatlantis/atlantis/merges",
58+
"archive_url": "https://api.github.com/repos/runatlantis/atlantis/{archive_format}{/ref}",
59+
"downloads_url": "https://api.github.com/repos/runatlantis/atlantis/downloads",
60+
"issues_url": "https://api.github.com/repos/runatlantis/atlantis/issues{/number}",
61+
"pulls_url": "https://api.github.com/repos/runatlantis/atlantis/pulls{/number}",
62+
"milestones_url": "https://api.github.com/repos/runatlantis/atlantis/milestones{/number}",
63+
"notifications_url": "https://api.github.com/repos/runatlantis/atlantis/notifications{?since,all,participating}",
64+
"labels_url": "https://api.github.com/repos/runatlantis/atlantis/labels{/name}",
65+
"releases_url": "https://api.github.com/repos/runatlantis/atlantis/releases{/id}",
66+
"deployments_url": "https://api.github.com/repos/runatlantis/atlantis/deployments",
67+
"created_at": "2019-01-23T17:58:45Z",
68+
"updated_at": "2019-02-08T21:46:28Z",
69+
"pushed_at": "2019-02-10T01:49:25Z",
70+
"git_url": "git://github.com/runatlantis/atlantis.git",
71+
"ssh_url": "[email protected]:runatlantis/atlantis.git",
72+
"clone_url": "https://github.com/runatlantis/atlantis.git",
73+
"svn_url": "https://github.com/runatlantis/atlantis",
74+
"homepage": null,
75+
"size": 32,
76+
"stargazers_count": 0,
77+
"watchers_count": 0,
78+
"language": "HCL",
79+
"has_issues": true,
80+
"has_projects": true,
81+
"has_downloads": true,
82+
"has_wiki": true,
83+
"has_pages": false,
84+
"forks_count": 0,
85+
"mirror_url": null,
86+
"archived": false,
87+
"open_issues_count": 1,
88+
"license": null,
89+
"forks": 0,
90+
"open_issues": 1,
91+
"watchers": 0,
92+
"default_branch": "master",
93+
"permissions": {
94+
"admin": true,
95+
"push": true,
96+
"pull": true
97+
},
98+
"allow_squash_merge": true,
99+
"allow_merge_commit": true,
100+
"allow_rebase_merge": true,
101+
"network_count": 0,
102+
"subscribers_count": 0
103+
}

server/events/vcs/github_client.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,37 @@ func (g *GithubClient) UpdateStatus(repo models.Repo, pull models.PullRequest, s
178178

179179
// MergePull merges the pull request.
180180
func (g *GithubClient) MergePull(pull models.PullRequest) error {
181+
// Users can set their repo to disallow certain types of merging.
182+
// We detect which types aren't allowed and use the type that is.
183+
repo, _, err := g.client.Repositories.Get(g.ctx, pull.BaseRepo.Owner, pull.BaseRepo.Name)
184+
if err != nil {
185+
return errors.Wrap(err, "fetching repo info")
186+
}
187+
const (
188+
defaultMergeMethod = "merge"
189+
rebaseMergeMethod = "rebase"
190+
squashMergeMethod = "squash"
191+
)
192+
method := defaultMergeMethod
193+
if !repo.GetAllowMergeCommit() {
194+
if repo.GetAllowRebaseMerge() {
195+
method = rebaseMergeMethod
196+
} else if repo.GetAllowSquashMerge() {
197+
method = squashMergeMethod
198+
}
199+
}
200+
201+
// Now we're ready to make our API call to merge the pull request.
202+
options := &github.PullRequestOptions{
203+
MergeMethod: method,
204+
}
181205
mergeResult, _, err := g.client.PullRequests.Merge(
182206
g.ctx,
183207
pull.BaseRepo.Owner,
184208
pull.BaseRepo.Name,
185209
pull.Num,
186210
common.AutomergeCommitMsg,
187-
nil)
211+
options)
188212
if err != nil {
189213
return errors.Wrap(err, "merging pull request")
190214
}

server/events/vcs/github_client_test.go

+127-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package vcs_test
22

33
import (
44
"crypto/tls"
5+
"encoding/json"
56
"fmt"
67
"io/ioutil"
78
"net/http"
@@ -287,7 +288,7 @@ func TestGithubClient_PullIsMergeable(t *testing.T) {
287288
}
288289
}
289290

290-
func TestGithubClient_MergePull(t *testing.T) {
291+
func TestGithubClient_MergePullHandlesError(t *testing.T) {
291292
cases := []struct {
292293
code int
293294
message string
@@ -312,15 +313,21 @@ func TestGithubClient_MergePull(t *testing.T) {
312313
},
313314
}
314315

316+
jsBytes, err := ioutil.ReadFile("fixtures/github-repo.json")
317+
Ok(t, err)
318+
315319
for _, c := range cases {
316320
t.Run(c.message, func(t *testing.T) {
317321
testServer := httptest.NewTLSServer(
318322
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
319323
switch r.RequestURI {
324+
case "/api/v3/repos/owner/repo":
325+
w.Write(jsBytes) // nolint: errcheck
326+
return
320327
case "/api/v3/repos/owner/repo/pulls/1/merge":
321328
body, err := ioutil.ReadAll(r.Body)
322329
Ok(t, err)
323-
exp := "{\"commit_message\":\"[Atlantis] Automatically merging after successful apply\"}\n"
330+
exp := "{\"commit_message\":\"[Atlantis] Automatically merging after successful apply\",\"merge_method\":\"merge\"}\n"
324331
Equals(t, exp, string(body))
325332
var resp string
326333
if c.code == 200 {
@@ -369,6 +376,124 @@ func TestGithubClient_MergePull(t *testing.T) {
369376
}
370377
}
371378

379+
// Test that if the pull request only allows a certain merge method that we
380+
// use that method
381+
func TestGithubClient_MergePullCorrectMethod(t *testing.T) {
382+
cases := map[string]struct {
383+
allowMerge bool
384+
allowRebase bool
385+
allowSquash bool
386+
expMethod string
387+
}{
388+
"all true": {
389+
allowMerge: true,
390+
allowRebase: true,
391+
allowSquash: true,
392+
expMethod: "merge",
393+
},
394+
"all false (edge case)": {
395+
allowMerge: false,
396+
allowRebase: false,
397+
allowSquash: false,
398+
expMethod: "merge",
399+
},
400+
"merge: false rebase: true squash: true": {
401+
allowMerge: false,
402+
allowRebase: true,
403+
allowSquash: true,
404+
expMethod: "rebase",
405+
},
406+
"merge: false rebase: false squash: true": {
407+
allowMerge: false,
408+
allowRebase: false,
409+
allowSquash: true,
410+
expMethod: "squash",
411+
},
412+
"merge: false rebase: true squash: false": {
413+
allowMerge: false,
414+
allowRebase: true,
415+
allowSquash: false,
416+
expMethod: "rebase",
417+
},
418+
}
419+
420+
for name, c := range cases {
421+
t.Run(name, func(t *testing.T) {
422+
423+
// Modify response.
424+
jsBytes, err := ioutil.ReadFile("fixtures/github-repo.json")
425+
Ok(t, err)
426+
resp := string(jsBytes)
427+
resp = strings.Replace(resp,
428+
`"allow_squash_merge": true`,
429+
fmt.Sprintf(`"allow_squash_merge": %t`, c.allowSquash),
430+
-1)
431+
resp = strings.Replace(resp,
432+
`"allow_merge_commit": true`,
433+
fmt.Sprintf(`"allow_merge_commit": %t`, c.allowMerge),
434+
-1)
435+
resp = strings.Replace(resp,
436+
`"allow_rebase_merge": true`,
437+
fmt.Sprintf(`"allow_rebase_merge": %t`, c.allowRebase),
438+
-1)
439+
440+
testServer := httptest.NewTLSServer(
441+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
442+
switch r.RequestURI {
443+
case "/api/v3/repos/runatlantis/atlantis":
444+
w.Write([]byte(resp)) // nolint: errcheck
445+
return
446+
case "/api/v3/repos/runatlantis/atlantis/pulls/1/merge":
447+
body, err := ioutil.ReadAll(r.Body)
448+
Ok(t, err)
449+
defer r.Body.Close() // nolint: errcheck
450+
type bodyJSON struct {
451+
CommitMessage string `json:"commit_message"`
452+
MergeMethod string `json:"merge_method"`
453+
}
454+
expBody := bodyJSON{
455+
CommitMessage: "[Atlantis] Automatically merging after successful apply",
456+
MergeMethod: c.expMethod,
457+
}
458+
expBytes, err := json.Marshal(expBody)
459+
Ok(t, err)
460+
Equals(t, string(expBytes)+"\n", string(body))
461+
462+
resp := `{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e","merged":true,"message":"Pull Request successfully merged"}`
463+
w.Write([]byte(resp)) // nolint: errcheck
464+
default:
465+
t.Errorf("got unexpected request at %q", r.RequestURI)
466+
http.Error(w, "not found", http.StatusNotFound)
467+
return
468+
}
469+
}))
470+
471+
testServerURL, err := url.Parse(testServer.URL)
472+
Ok(t, err)
473+
client, err := vcs.NewGithubClient(testServerURL.Host, "user", "pass")
474+
Ok(t, err)
475+
defer disableSSLVerification()()
476+
477+
err = client.MergePull(
478+
models.PullRequest{
479+
BaseRepo: models.Repo{
480+
FullName: "runatlantis/atlantis",
481+
Owner: "runatlantis",
482+
Name: "atlantis",
483+
CloneURL: "",
484+
SanitizedCloneURL: "",
485+
VCSHost: models.VCSHost{
486+
Type: models.Github,
487+
Hostname: "github.com",
488+
},
489+
},
490+
Num: 1,
491+
})
492+
Ok(t, err)
493+
})
494+
}
495+
}
496+
372497
// disableSSLVerification disables ssl verification for the global http client
373498
// and returns a function to be called in a defer that will re-enable it.
374499
func disableSSLVerification() func() {

0 commit comments

Comments
 (0)