Skip to content

Commit 102addc

Browse files
authored
Merge pull request #457 from runatlantis/automerge
Add automerge capability
2 parents 2c6b87d + 74e9bbb commit 102addc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2480
-648
lines changed

.circleci/config.yml

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
- checkout
99
- run: make test-coverage
1010
- run: make check-fmt
11-
- run: make check-gometalint
11+
- run: make check-lint
1212
- run:
1313
name: post coverage to codecov.io
1414
command: bash <(curl -s https://codecov.io/bash)
@@ -42,9 +42,10 @@ jobs:
4242
# We use dockerize -wait here to wait until the server is up.
4343
- run: |
4444
dockerize -wait tcp://localhost:8080 -- \
45-
muffet -e 'https://github\\.com/runatlantis/atlantis/edit/master/.*' \
46-
-e 'https://github.com/helm/charts/tree/master/stable/atlantis#customization' \
47-
http://localhost:8080/
45+
muffet \
46+
-e 'https://github\.com/runatlantis/atlantis/edit/master/.*' \
47+
-e 'https://github.com/helm/charts/tree/master/stable/atlantis#customization' \
48+
http://localhost:8080/
4849
4950
# Build and push 'latest' Docker tag.
5051
docker_master:

.golangci.yml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
linters:
2+
enable:
3+
- deadcode
4+
- errcheck
5+
- gochecknoinits
6+
# We don't use goconst because it gives false positives in the tests.
7+
# - goconst
8+
- gofmt
9+
- golint
10+
- gosec
11+
- gosimple
12+
- ineffassign
13+
- interfacer
14+
- staticcheck
15+
- structcheck
16+
- typecheck
17+
- unconvert
18+
- unused
19+
- varcheck
20+
- vet
21+
- vetshadow

.gometalinter.json

-44
This file was deleted.

Makefile

+6-13
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,12 @@ release: ## Create packages for a release
6060
fmt: ## Run goimports (which also formats)
6161
goimports -w $$(find . -type f -name '*.go' ! -path "./vendor/*" ! -path "./server/static/bindata_assetfs.go" ! -path "**/mocks/*")
6262

63-
gometalint: ## Run every linter ever
64-
# gotype and gotypex are disabled because they don't pass on CI and https://github.com/alecthomas/gometalinter/issues/206
65-
# maligned is disabled because I'd rather have alphabetical struct fields than save a few bytes
66-
# gocyclo is temporarily disabled because we don't pass it right now
67-
# golint is temporarily disabled because we need to add comments everywhere first
68-
# CGO_ENABLED=0 is attempted workaround for https://github.com/alecthomas/gometalinter/issues/149
69-
CGO_ENABLED=0 gometalinter --config=.gometalinter.json ./...
70-
71-
gometalint-install: ## Install gometalint
72-
go get -u github.com/alecthomas/gometalinter
73-
gometalinter --install
74-
75-
check-gometalint: gometalint-install gometalint
63+
lint: ## Run linter
64+
golangci-lint run
65+
66+
check-lint:
67+
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b ./bin v1.13.2
68+
./bin/golangci-lint run
7669

7770
check-fmt: ## Fail if not formatted
7871
go get golang.org/x/tools/cmd/goimports

cmd/server.go

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const (
3939
AllowForkPRsFlag = "allow-fork-prs"
4040
AllowRepoConfigFlag = "allow-repo-config"
4141
AtlantisURLFlag = "atlantis-url"
42+
AutomergeFlag = "automerge"
4243
BitbucketBaseURLFlag = "bitbucket-base-url"
4344
BitbucketTokenFlag = "bitbucket-token"
4445
BitbucketUserFlag = "bitbucket-user"
@@ -205,6 +206,11 @@ var boolFlags = []boolFlag{
205206
" on the Atlantis server.",
206207
defaultValue: false,
207208
},
209+
{
210+
name: AutomergeFlag,
211+
description: "Automatically merge pull requests when all plans are successfully applied.",
212+
defaultValue: false,
213+
},
208214
{
209215
name: RequireApprovalFlag,
210216
description: "Require pull requests to be \"Approved\" before allowing the apply command to be run.",

cmd/server_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ func TestExecute_Defaults(t *testing.T) {
333333
Equals(t, "http://"+hostname+":4141", passedConfig.AtlantisURL)
334334
Equals(t, false, passedConfig.AllowForkPRs)
335335
Equals(t, false, passedConfig.AllowRepoConfig)
336+
Equals(t, false, passedConfig.Automerge)
336337

337338
// Get our home dir since that's what gets defaulted to
338339
dataDir, err := homedir.Expand("~/.atlantis")
@@ -438,6 +439,7 @@ func TestExecute_Flags(t *testing.T) {
438439
cmd.AtlantisURLFlag: "url",
439440
cmd.AllowForkPRsFlag: true,
440441
cmd.AllowRepoConfigFlag: true,
442+
cmd.AutomergeFlag: true,
441443
cmd.BitbucketBaseURLFlag: "https://bitbucket-base-url.com",
442444
cmd.BitbucketTokenFlag: "bitbucket-token",
443445
cmd.BitbucketUserFlag: "bitbucket-user",
@@ -468,6 +470,7 @@ func TestExecute_Flags(t *testing.T) {
468470
Equals(t, "url", passedConfig.AtlantisURL)
469471
Equals(t, true, passedConfig.AllowForkPRs)
470472
Equals(t, true, passedConfig.AllowRepoConfig)
473+
Equals(t, true, passedConfig.Automerge)
471474
Equals(t, "https://bitbucket-base-url.com", passedConfig.BitbucketBaseURL)
472475
Equals(t, "bitbucket-token", passedConfig.BitbucketToken)
473476
Equals(t, "bitbucket-user", passedConfig.BitbucketUser)
@@ -499,6 +502,7 @@ func TestExecute_ConfigFile(t *testing.T) {
499502
atlantis-url: "url"
500503
allow-fork-prs: true
501504
allow-repo-config: true
505+
automerge: true
502506
bitbucket-base-url: "https://mydomain.com"
503507
bitbucket-token: "bitbucket-token"
504508
bitbucket-user: "bitbucket-user"
@@ -533,6 +537,7 @@ tfe-token: my-token
533537
Equals(t, "url", passedConfig.AtlantisURL)
534538
Equals(t, true, passedConfig.AllowForkPRs)
535539
Equals(t, true, passedConfig.AllowRepoConfig)
540+
Equals(t, true, passedConfig.Automerge)
536541
Equals(t, "https://mydomain.com", passedConfig.BitbucketBaseURL)
537542
Equals(t, "bitbucket-token", passedConfig.BitbucketToken)
538543
Equals(t, "bitbucket-user", passedConfig.BitbucketUser)
@@ -564,6 +569,7 @@ func TestExecute_EnvironmentOverride(t *testing.T) {
564569
atlantis-url: "url"
565570
allow-fork-prs: true
566571
allow-repo-config: true
572+
automerge: true
567573
bitbucket-base-url: "https://mydomain.com"
568574
bitbucket-token: "bitbucket-token"
569575
bitbucket-user: "bitbucket-user"
@@ -594,6 +600,7 @@ tfe-token: my-token
594600
"ATLANTIS_URL": "override-url",
595601
"ALLOW_FORK_PRS": "false",
596602
"ALLOW_REPO_CONFIG": "false",
603+
"AUTOMERGE": "false",
597604
"BITBUCKET_BASE_URL": "https://override-bitbucket-base-url",
598605
"BITBUCKET_TOKEN": "override-bitbucket-token",
599606
"BITBUCKET_USER": "override-bitbucket-user",
@@ -628,6 +635,7 @@ tfe-token: my-token
628635
Equals(t, "override-url", passedConfig.AtlantisURL)
629636
Equals(t, false, passedConfig.AllowForkPRs)
630637
Equals(t, false, passedConfig.AllowRepoConfig)
638+
Equals(t, false, passedConfig.Automerge)
631639
Equals(t, "https://override-bitbucket-base-url", passedConfig.BitbucketBaseURL)
632640
Equals(t, "override-bitbucket-token", passedConfig.BitbucketToken)
633641
Equals(t, "override-bitbucket-user", passedConfig.BitbucketUser)
@@ -659,6 +667,7 @@ func TestExecute_FlagConfigOverride(t *testing.T) {
659667
atlantis-url: "url"
660668
allow-fork-prs: true
661669
allow-repo-config: true
670+
automerge: true
662671
bitbucket-base-url: "https://bitbucket-base-url"
663672
bitbucket-token: "bitbucket-token"
664673
bitbucket-user: "bitbucket-user"
@@ -689,6 +698,7 @@ tfe-token: my-token
689698
cmd.AtlantisURLFlag: "override-url",
690699
cmd.AllowForkPRsFlag: false,
691700
cmd.AllowRepoConfigFlag: false,
701+
cmd.AutomergeFlag: false,
692702
cmd.BitbucketBaseURLFlag: "https://override-bitbucket-base-url",
693703
cmd.BitbucketTokenFlag: "override-bitbucket-token",
694704
cmd.BitbucketUserFlag: "override-bitbucket-user",
@@ -717,6 +727,7 @@ tfe-token: my-token
717727
Ok(t, err)
718728
Equals(t, "override-url", passedConfig.AtlantisURL)
719729
Equals(t, false, passedConfig.AllowForkPRs)
730+
Equals(t, false, passedConfig.Automerge)
720731
Equals(t, "https://override-bitbucket-base-url", passedConfig.BitbucketBaseURL)
721732
Equals(t, "override-bitbucket-token", passedConfig.BitbucketToken)
722733
Equals(t, "override-bitbucket-user", passedConfig.BitbucketUser)
@@ -750,6 +761,7 @@ func TestExecute_FlagEnvVarOverride(t *testing.T) {
750761
"ATLANTIS_URL": "url",
751762
"ALLOW_FORK_PRS": "true",
752763
"ALLOW_REPO_CONFIG": "true",
764+
"AUTOMERGE": "true",
753765
"BITBUCKET_BASE_URL": "https://bitbucket-base-url",
754766
"BITBUCKET_TOKEN": "bitbucket-token",
755767
"BITBUCKET_USER": "bitbucket-user",
@@ -788,6 +800,7 @@ func TestExecute_FlagEnvVarOverride(t *testing.T) {
788800
cmd.AtlantisURLFlag: "override-url",
789801
cmd.AllowForkPRsFlag: false,
790802
cmd.AllowRepoConfigFlag: false,
803+
cmd.AutomergeFlag: false,
791804
cmd.BitbucketBaseURLFlag: "https://override-bitbucket-base-url",
792805
cmd.BitbucketTokenFlag: "override-bitbucket-token",
793806
cmd.BitbucketUserFlag: "override-bitbucket-user",
@@ -818,6 +831,7 @@ func TestExecute_FlagEnvVarOverride(t *testing.T) {
818831
Equals(t, "override-url", passedConfig.AtlantisURL)
819832
Equals(t, false, passedConfig.AllowForkPRs)
820833
Equals(t, false, passedConfig.AllowRepoConfig)
834+
Equals(t, false, passedConfig.Automerge)
821835
Equals(t, "https://override-bitbucket-base-url", passedConfig.BitbucketBaseURL)
822836
Equals(t, "override-bitbucket-token", passedConfig.BitbucketToken)
823837
Equals(t, "override-bitbucket-user", passedConfig.BitbucketUser)

runatlantis.io/.vuepress/config.js

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ module.exports = {
8181
['how-atlantis-works', 'Overview'],
8282
'locking',
8383
'autoplanning',
84+
'automerging',
8485
'checkout-strategy',
8586
'security'
8687
]

runatlantis.io/docs/atlantis-yaml-reference.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ to use `atlantis.yaml` files.
1616
## Example Using All Keys
1717
```yaml
1818
version: 2
19+
automerge: true
1920
projects:
2021
- name: my-project-name
2122
dir: .
@@ -67,14 +68,16 @@ It should be noted that `atlantis apply` itself could be exploited if run on a m
6768
### Top-Level Keys
6869
```yaml
6970
version:
71+
automerge:
7072
projects:
7173
workflows:
7274
```
73-
| Key | Type | Default | Required | Description |
74-
| --------- | ---------------------------------------------------------------- | ------- | -------- | ------------------------------------------- |
75-
| version | int | none | yes | This key is required and must be set to `2` |
76-
| projects | array[[Project](atlantis-yaml-reference.html#project)] | [] | no | Lists the projects in this repo |
77-
| workflows | map[string -> [Workflow](atlantis-yaml-reference.html#workflow)] | {} | no | Custom workflows |
75+
| Key | Type | Default | Required | Description |
76+
| --------- | ---------------------------------------------------------------- | ------- | -------- | ----------------------------------------------------------- |
77+
| version | int | none | yes | This key is required and must be set to `2` |
78+
| automerge | bool | false | no | Automatically merge pull request when all plans are applied |
79+
| projects | array[[Project](atlantis-yaml-reference.html#project)] | [] | no | Lists the projects in this repo |
80+
| workflows | map[string -> [Workflow](atlantis-yaml-reference.html#workflow)] | {} | no | Custom workflows |
7881

7982
### Project
8083
```yaml

runatlantis.io/docs/automerging.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Automerging
2+
Atlantis can be configured to automatically merge a pull request after all plans have
3+
been successfully applied.
4+
5+
6+
![Automerge](./images/automerge.png)
7+
8+
## How To Enable
9+
Automerging can be enabled either by:
10+
1. Passing the `--automerge` flag to `atlantis server`. This will cause all
11+
pull requests to be automerged and any repo config will be ignored.
12+
1. Setting `automerge: true` in the repo's `atlantis.yaml` file:
13+
```yaml
14+
version: 2
15+
automerge: true
16+
projects:
17+
- dir: .
18+
```
19+
:::tip NOTE
20+
If a repo has an `atlantis.yaml` file, then each project in the repo needs
21+
to be configured under the `projects` key.
22+
:::
23+
24+
## All Plans Must Succeed
25+
When automerge is enabled, **all plans** in a pull request **must succeed** before
26+
**any** plans can be applied.
27+
28+
For example, imagine this scenario:
29+
1. I open a pull request that makes changes to two Terraform projects, in `dir1/`
30+
and `dir2/`.
31+
1. The plan for `dir2/` fails because my Terraform syntax is wrong.
32+
33+
In this scenario, I can't run
34+
```
35+
atlantis apply -d dir1
36+
```
37+
Even though that plan succeeded, because **all** plans must succeed for **any** plans
38+
to be saved.
39+
40+
Once I fix the issue in `dir2`, I can push a new commit which will trigger an
41+
autoplan. Then I will be able to apply both plans.
42+
43+
## Permissions
44+
The Atlantis VCS user must have the ability to merge pull requests.
83.6 KB
Loading

server/events/command_result.go

+21-1
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,29 @@
1313

1414
package events
1515

16+
import "github.com/runatlantis/atlantis/server/events/models"
17+
1618
// CommandResult is the result of running a Command.
1719
type CommandResult struct {
1820
Error error
1921
Failure string
20-
ProjectResults []ProjectResult
22+
ProjectResults []models.ProjectResult
23+
// PlansDeleted is true if all plans created during this command were
24+
// deleted. This happens if automerging is enabled and one project has an
25+
// error since automerging requires all plans to succeed.
26+
PlansDeleted bool
27+
}
28+
29+
// HasErrors returns true if there were any errors during the execution,
30+
// even if it was only in one project.
31+
func (c CommandResult) HasErrors() bool {
32+
if c.Error != nil || c.Failure != "" {
33+
return true
34+
}
35+
for _, r := range c.ProjectResults {
36+
if !r.IsSuccessful() {
37+
return true
38+
}
39+
}
40+
return false
2141
}

0 commit comments

Comments
 (0)