Skip to content

Commit a2c43c0

Browse files
committed
feat: add GitLab squash merge support for automerge
- Add merge method support to GitLab client for squash merging - Parse merge method from PullRequestOptions in DiscardReviews and MergePull - Map 'squash' merge method to GitLab API squash parameter - Add comprehensive test coverage for merge method handling - Follow established GitLab client test patterns with json.NewDecoder - Update documentation to reflect GitLab squash merge support Fixes #5415 Signed-off-by: geoff-kruss <[email protected]>
1 parent 2b2a845 commit a2c43c0

File tree

4 files changed

+108
-4
lines changed

4 files changed

+108
-4
lines changed

runatlantis.io/docs/using-atlantis.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ atlantis apply -w staging
179179
* `-p project` Apply the plan for this project. Refers to the name of the project configured in the repo's [`atlantis.yaml` file](repo-level-atlantis-yaml.md). Cannot be used at same time as `-d` or `-w`.
180180
* `-w workspace` Apply the plan for this [Terraform workspace](https://developer.hashicorp.com/terraform/language/state/workspaces). Ignore this if Terraform workspaces are unused.
181181
* `--auto-merge-disabled` Disable [automerge](automerging.md) for this apply command.
182-
* `--auto-merge-method method` Specify which [merge method](automerging.md#how-to-set-the-merge-method-for-automerge) use for the apply command if [automerge](automerging.md) is enabled. Implemented only for GitHub.
182+
* `--auto-merge-method method` Specify which [merge method](automerging.md#how-to-set-the-merge-method-for-automerge) to use for the apply command if [automerge](automerging.md) is enabled. Supported for GitHub and GitLab VCS providers.
183183
* `--verbose` Append Atlantis log to comment.
184184

185185
### Additional Terraform flags

server/events/models/models.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,13 @@ type PullRequest struct {
186186
BaseRepo Repo
187187
}
188188

189-
// PullRequestOptions is used to set optional paralmeters for PullRequest
189+
// PullRequestOptions is used to set optional parameters for PullRequest
190190
type PullRequestOptions struct {
191191
// When DeleteSourceBranchOnMerge flag is set to true VCS deletes the source branch after the PR is merged
192192
// Applied by GitLab & AzureDevops
193193
DeleteSourceBranchOnMerge bool
194-
// MergeMethod specifies the merge method for the VCS
195-
// Implemented only for Github
194+
// MergeMethod specifies the merge method for the VCS (merge, squash, rebase)
195+
// Supported by GitHub and GitLab VCS providers
196196
MergeMethod string
197197
}
198198

server/events/vcs/gitlab_client.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,12 +612,19 @@ func (g *GitlabClient) MergePull(logger logging.SimpleLogging, pull models.PullR
612612
g.WaitForSuccessPipeline(logger, context.Background(), pull)
613613
}
614614

615+
// Handle merge method
616+
// Gitlab currently only supports squash as a merge method
617+
// via a dedicated field
618+
// https://docs.gitlab.com/api/merge_requests/#merge-a-merge-request
619+
squash := pullOptions.MergeMethod == "squash"
620+
615621
_, resp, err = g.Client.MergeRequests.AcceptMergeRequest(
616622
pull.BaseRepo.FullName,
617623
pull.Num,
618624
&gitlab.AcceptMergeRequestOptions{
619625
MergeCommitMessage: &commitMsg,
620626
ShouldRemoveSourceBranch: &pullOptions.DeleteSourceBranchOnMerge,
627+
Squash: &squash,
621628
})
622629
if resp != nil {
623630
logger.Debug("PUT /projects/%s/merge_requests/%d/merge returned: %d", pull.BaseRepo.FullName, pull.Num, resp.StatusCode)

server/events/vcs/gitlab_client_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,103 @@ func TestGitlabClient_MergePull(t *testing.T) {
300300
}
301301
}
302302

303+
func TestGitlabClient_MergePullSquash(t *testing.T) {
304+
logger := logging.NewNoopLogger(t)
305+
mergeSuccess, err := os.ReadFile("testdata/github-pull-request.json")
306+
Ok(t, err)
307+
308+
pipelineSuccess, err := os.ReadFile("testdata/gitlab-pipeline-success.json")
309+
Ok(t, err)
310+
311+
projectSuccess, err := os.ReadFile("testdata/gitlab-project-success.json")
312+
Ok(t, err)
313+
314+
cases := []struct {
315+
description string
316+
mergeMethod string
317+
expSquash bool
318+
}{
319+
{
320+
"squash merge method",
321+
"squash",
322+
true,
323+
},
324+
{
325+
"merge method (default)",
326+
"merge",
327+
false,
328+
},
329+
{
330+
"empty merge method",
331+
"",
332+
false,
333+
},
334+
}
335+
336+
for _, c := range cases {
337+
t.Run(c.description, func(t *testing.T) {
338+
var mergeRequestBody map[string]interface{}
339+
testServer := httptest.NewServer(
340+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
341+
switch r.RequestURI {
342+
// The first request should hit this URL.
343+
case "/api/v4/projects/runatlantis%2Fatlantis/merge_requests/1/merge":
344+
// Capture the request body to verify squash parameter
345+
json.NewDecoder(r.Body).Decode(&mergeRequestBody) // nolint: errcheck
346+
w.WriteHeader(http.StatusOK)
347+
w.Write(mergeSuccess) // nolint: errcheck
348+
case "/api/v4/projects/runatlantis%2Fatlantis/merge_requests/1":
349+
w.WriteHeader(http.StatusOK)
350+
w.Write(pipelineSuccess) // nolint: errcheck
351+
case "/api/v4/projects/4580910":
352+
w.WriteHeader(http.StatusOK)
353+
w.Write(projectSuccess) // nolint: errcheck
354+
case "/api/v4/":
355+
// Rate limiter requests.
356+
w.WriteHeader(http.StatusOK)
357+
default:
358+
t.Errorf("got unexpected request at %q", r.RequestURI)
359+
http.Error(w, "not found", http.StatusNotFound)
360+
}
361+
}))
362+
363+
internalClient, err := gitlab.NewClient("token", gitlab.WithBaseURL(testServer.URL))
364+
Ok(t, err)
365+
client := &GitlabClient{
366+
Client: internalClient,
367+
Version: nil,
368+
}
369+
370+
err = client.MergePull(
371+
logger,
372+
models.PullRequest{
373+
Num: 1,
374+
BaseRepo: models.Repo{
375+
FullName: "runatlantis/atlantis",
376+
Owner: "runatlantis",
377+
Name: "atlantis",
378+
},
379+
}, models.PullRequestOptions{
380+
DeleteSourceBranchOnMerge: false,
381+
MergeMethod: c.mergeMethod,
382+
})
383+
Ok(t, err)
384+
385+
// Verify that the squash parameter was sent correctly
386+
if c.expSquash {
387+
squashValue, exists := mergeRequestBody["squash"]
388+
Assert(t, exists, "Expected squash parameter in request body")
389+
Assert(t, squashValue == true, "Expected squash:true in request body, got: %v", squashValue)
390+
} else {
391+
// When not squashing, the squash parameter should be false or not present
392+
if squashValue, exists := mergeRequestBody["squash"]; exists {
393+
Assert(t, squashValue == false, "Expected squash:false in request body, got: %v", squashValue)
394+
}
395+
}
396+
})
397+
}
398+
}
399+
303400
func TestGitlabClient_UpdateStatus(t *testing.T) {
304401
logger := logging.NewNoopLogger(t)
305402

0 commit comments

Comments
 (0)