Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion runatlantis.io/docs/using-atlantis.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ atlantis apply -w staging
* `-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`.
* `-w workspace` Apply the plan for this [Terraform workspace](https://developer.hashicorp.com/terraform/language/state/workspaces). Ignore this if Terraform workspaces are unused.
* `--auto-merge-disabled` Disable [automerge](automerging.md) for this apply command.
* `--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.
* `--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.
* `--verbose` Append Atlantis log to comment.

### Additional Terraform flags
Expand Down
6 changes: 3 additions & 3 deletions server/events/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,13 @@ type PullRequest struct {
BaseRepo Repo
}

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

Expand Down
7 changes: 7 additions & 0 deletions server/events/vcs/gitlab_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,12 +612,19 @@
g.WaitForSuccessPipeline(logger, context.Background(), pull)
}

// Handle merge method
// Gitlab currently only supports squash as a merge method
// via a dedicated field
// https://docs.gitlab.com/api/merge_requests/#merge-a-merge-request
squash := pullOptions.MergeMethod == "squash"

Check failure on line 620 in server/events/vcs/gitlab_client.go

View workflow job for this annotation

GitHub Actions / Linting

File is not properly formatted (gofmt)
_, resp, err = g.Client.MergeRequests.AcceptMergeRequest(
pull.BaseRepo.FullName,
pull.Num,
&gitlab.AcceptMergeRequestOptions{
MergeCommitMessage: &commitMsg,
ShouldRemoveSourceBranch: &pullOptions.DeleteSourceBranchOnMerge,
Squash: &squash,
})
if resp != nil {
logger.Debug("PUT /projects/%s/merge_requests/%d/merge returned: %d", pull.BaseRepo.FullName, pull.Num, resp.StatusCode)
Expand Down
97 changes: 97 additions & 0 deletions server/events/vcs/gitlab_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,103 @@
}
}

func TestGitlabClient_MergePullSquash(t *testing.T) {
logger := logging.NewNoopLogger(t)
mergeSuccess, err := os.ReadFile("testdata/github-pull-request.json")
Ok(t, err)

pipelineSuccess, err := os.ReadFile("testdata/gitlab-pipeline-success.json")
Ok(t, err)

projectSuccess, err := os.ReadFile("testdata/gitlab-project-success.json")
Ok(t, err)

cases := []struct {
description string
mergeMethod string
expSquash bool
}{
{
"squash merge method",
"squash",
true,
},
{
"merge method (default)",
"merge",

Check failure on line 326 in server/events/vcs/gitlab_client_test.go

View workflow job for this annotation

GitHub Actions / Linting

File is not properly formatted (gofmt)
false,
},
{
"empty merge method",
"",
false,
},
}

for _, c := range cases {
t.Run(c.description, func(t *testing.T) {
var mergeRequestBody map[string]interface{}
testServer := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.RequestURI {
// The first request should hit this URL.
case "/api/v4/projects/runatlantis%2Fatlantis/merge_requests/1/merge":
// Capture the request body to verify squash parameter
json.NewDecoder(r.Body).Decode(&mergeRequestBody) // nolint: errcheck
w.WriteHeader(http.StatusOK)
w.Write(mergeSuccess) // nolint: errcheck
case "/api/v4/projects/runatlantis%2Fatlantis/merge_requests/1":
w.WriteHeader(http.StatusOK)
w.Write(pipelineSuccess) // nolint: errcheck
case "/api/v4/projects/4580910":
w.WriteHeader(http.StatusOK)
w.Write(projectSuccess) // nolint: errcheck
case "/api/v4/":
// Rate limiter requests.
w.WriteHeader(http.StatusOK)
default:
t.Errorf("got unexpected request at %q", r.RequestURI)
http.Error(w, "not found", http.StatusNotFound)
}
}))

internalClient, err := gitlab.NewClient("token", gitlab.WithBaseURL(testServer.URL))
Ok(t, err)
client := &GitlabClient{
Client: internalClient,
Version: nil,
}

err = client.MergePull(
logger,
models.PullRequest{
Num: 1,
BaseRepo: models.Repo{
FullName: "runatlantis/atlantis",
Owner: "runatlantis",
Name: "atlantis",
},
}, models.PullRequestOptions{
DeleteSourceBranchOnMerge: false,
MergeMethod: c.mergeMethod,
})
Ok(t, err)

// Verify that the squash parameter was sent correctly
if c.expSquash {
squashValue, exists := mergeRequestBody["squash"]
Assert(t, exists, "Expected squash parameter in request body")
Assert(t, squashValue == true, "Expected squash:true in request body, got: %v", squashValue)
} else {
// When not squashing, the squash parameter should be false or not present
if squashValue, exists := mergeRequestBody["squash"]; exists {
Assert(t, squashValue == false, "Expected squash:false in request body, got: %v", squashValue)
}
}
})
}
}

func TestGitlabClient_UpdateStatus(t *testing.T) {
logger := logging.NewNoopLogger(t)

Expand Down
Loading