Skip to content
This repository has been archived by the owner on Oct 6, 2021. It is now read-only.

Commit

Permalink
Add delete source branch on merge.
Browse files Browse the repository at this point in the history
This is based on an abandoned PR (runatlantis#1189) from Zack Arnold (zparnold).
  • Loading branch information
magne committed Oct 13, 2020
1 parent 410bafd commit 4b9b456
Show file tree
Hide file tree
Showing 14 changed files with 330 additions and 51 deletions.
4 changes: 3 additions & 1 deletion build_release.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env bash

VERSION=${1:-0.15.1-betr}
VERSION=${1:-0.15.2-betr}

docker run --rm -v $(pwd):/go/src/github.com/runatlantis/atlantis -w /go/src/github.com/runatlantis/atlantis runatlantis/testing-env make test

env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o atlantis

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ require (
github.com/imdario/mergo v0.3.5 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/magiconair/properties v1.7.3 // indirect
github.com/mcdafydd/go-azuredevops v0.11.1
github.com/mcdafydd/go-azuredevops v0.11.2
github.com/microcosm-cc/bluemonday v1.0.1
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
github.com/mitchellh/go-homedir v1.0.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mcdafydd/go-azuredevops v0.11.1 h1:NO4wlkyFpdxqZZzNzn5m3fJc4box0jnkC8LBhAaPXeA=
github.com/mcdafydd/go-azuredevops v0.11.1/go.mod h1:B4UDyn7WEj1/97f45j3VnzEfkWKe05+/dCcAPdOET4A=
github.com/mcdafydd/go-azuredevops v0.11.2 h1:M8t1io1bfUD7N1ZQHJVk3DvvEBy41FNkYgIwWoQnywk=
github.com/mcdafydd/go-azuredevops v0.11.2/go.mod h1:B4UDyn7WEj1/97f45j3VnzEfkWKe05+/dCcAPdOET4A=
github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 h1:KHyL+3mQOF9sPfs26lsefckcFNDcIZtiACQiECzIUkw=
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/spf13/viper"
)

const atlantisVersion = "0.15.1-betr"
const atlantisVersion = "0.15.2-betr"

func main() {
v := viper.New()
Expand Down
13 changes: 11 additions & 2 deletions server/events/command_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHead
c.updateCommitStatus(ctx, cmd.Name, pullStatus)

if cmd.Name == models.ApplyCommand && c.automergeEnabled(ctx, projectCmds) {
c.automerge(ctx, pullStatus)
c.automerge(ctx, pullStatus, c.deleteSourceBranchOnMerge(ctx, projectCmds))
}
}

Expand Down Expand Up @@ -371,7 +371,7 @@ func (c *DefaultCommandRunner) updateCommitStatus(ctx *CommandContext, cmd model
}
}

func (c *DefaultCommandRunner) automerge(ctx *CommandContext, pullStatus models.PullStatus) {
func (c *DefaultCommandRunner) automerge(ctx *CommandContext, pullStatus models.PullStatus, shouldDeleteSourceBranch bool) {
// We only automerge if all projects have been successfully applied.
for _, p := range pullStatus.Projects {
if p.Status != models.AppliedPlanStatus {
Expand All @@ -388,6 +388,9 @@ func (c *DefaultCommandRunner) automerge(ctx *CommandContext, pullStatus models.

// Make the API call to perform the merge.
ctx.Log.Info("automerging pull request")
if shouldDeleteSourceBranch {
ctx.Pull.DeleteSourceBranchOnMerge = true
}
err := c.VCSClient.MergePull(ctx.Pull)

if err != nil {
Expand Down Expand Up @@ -593,6 +596,12 @@ func (c *DefaultCommandRunner) automergeEnabled(ctx *CommandContext, projectCmds
(len(projectCmds) > 0 && projectCmds[0].AutomergeEnabled)
}

// deleteSourceBranchOnMerge returns true if we should delete the source branch on merge in this context.
func (c *DefaultCommandRunner) deleteSourceBranchOnMerge(ctx *CommandContext, projectCmds []models.ProjectCommandContext) bool {
// check if this repo is configured for automerging.
return (len(projectCmds) > 0 && projectCmds[0].DeleteSourceBranchOnMerge)
}

// parallelApplyEnabled returns true if parallel apply is enabled in this context.
func (c *DefaultCommandRunner) parallelApplyEnabled(ctx *CommandContext, projectCmds []models.ProjectCommandContext) bool {
return len(projectCmds) > 0 && projectCmds[0].ParallelApplyEnabled
Expand Down
4 changes: 4 additions & 0 deletions server/events/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ type PullRequest struct {
State PullRequestState
// BaseRepo is the repository that the pull request will be merged into.
BaseRepo Repo
// DeleteSourceBranchOnMerge will set a field that tells the VCS to delete the source branch when the PR is merged
DeleteSourceBranchOnMerge bool
}

type PullRequestState int
Expand Down Expand Up @@ -317,6 +319,8 @@ type ProjectCommandContext struct {
AutoplanEnabled bool
// BaseRepo is the repository that the pull request will be merged into.
BaseRepo Repo
// DeleteSourceBranchOnMerge will attempt to allow a branch to be deleted when merged (AzureDevOps Support Only)
DeleteSourceBranchOnMerge bool
// EscapedCommentArgs are the extra arguments that were added to the atlantis
// command, ex. atlantis plan -- -target=resource. We then escape them
// by adding a \ before each character so that they can be used within
Expand Down
56 changes: 31 additions & 25 deletions server/events/project_command_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const (
DefaultParallelApplyEnabled = false
// DefaultParallelPlanEnabled is the default for the parallel plan setting.
DefaultParallelPlanEnabled = false
// DefaultDeleteSourceBranchOnMerge being false is the default setting whether or not to remove a source branch on merge
DefaultDeleteSourceBranchOnMerge = false
)

//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_project_command_builder.go ProjectCommandBuilder
Expand Down Expand Up @@ -174,7 +176,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *CommandContext,
for _, mp := range matchingProjects {
ctx.Log.Debug("determining config for project at dir: %q workspace: %q", mp.Dir, mp.Workspace)
mergedCfg := p.GlobalCfg.MergeProjectCfg(ctx.Log, ctx.BaseRepo.ID(), mp, repoCfg)
projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, mergedCfg, commentFlags, repoCfg.Automerge, repoCfg.ParallelApply, repoCfg.ParallelPlan, verbose, repoDir))
projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, mergedCfg, commentFlags, repoCfg.Automerge, repoCfg.ParallelApply, repoCfg.ParallelPlan, verbose, repoDir, repoCfg.DeleteSourceBranchOnMerge))
}
} else {
// If there is no config file, then we'll plan each project that
Expand All @@ -185,7 +187,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *CommandContext,
for _, mp := range modifiedProjects {
ctx.Log.Debug("determining config for project at dir: %q", mp.Path)
pCfg := p.GlobalCfg.DefaultProjCfg(ctx.Log, ctx.BaseRepo.ID(), mp.Path, DefaultWorkspace)
projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, pCfg, commentFlags, DefaultAutomergeEnabled, DefaultParallelApplyEnabled, DefaultParallelPlanEnabled, verbose, repoDir))
projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, pCfg, commentFlags, DefaultAutomergeEnabled, DefaultParallelApplyEnabled, DefaultParallelPlanEnabled, verbose, repoDir, DefaultDeleteSourceBranchOnMerge))
}
}

Expand Down Expand Up @@ -320,12 +322,14 @@ func (p *DefaultProjectCommandBuilder) buildProjectCommandCtx(
automerge := DefaultAutomergeEnabled
parallelApply := DefaultParallelApplyEnabled
parallelPlan := DefaultParallelPlanEnabled
deleteBranchOnMerge := DefaultDeleteSourceBranchOnMerge
if repoCfgPtr != nil {
automerge = repoCfgPtr.Automerge
parallelApply = repoCfgPtr.ParallelApply
parallelPlan = repoCfgPtr.ParallelPlan
deleteBranchOnMerge = repoCfgPtr.DeleteSourceBranchOnMerge
}
return p.buildCtx(ctx, cmd, projCfg, commentFlags, automerge, parallelApply, parallelPlan, verbose, repoDir), nil
return p.buildCtx(ctx, cmd, projCfg, commentFlags, automerge, parallelApply, parallelPlan, verbose, repoDir, deleteBranchOnMerge), nil
}

// getCfg returns the atlantis.yaml config (if it exists) for this project. If
Expand Down Expand Up @@ -417,7 +421,8 @@ func (p *DefaultProjectCommandBuilder) buildCtx(ctx *CommandContext,
parallelApplyEnabled bool,
parallelPlanEnabled bool,
verbose bool,
absRepoDir string) models.ProjectCommandContext {
absRepoDir string,
deleteBranchOnMerge bool) models.ProjectCommandContext {

var steps []valid.Step
switch cmd {
Expand All @@ -434,27 +439,28 @@ func (p *DefaultProjectCommandBuilder) buildCtx(ctx *CommandContext,
}

return models.ProjectCommandContext{
ApplyCmd: p.CommentBuilder.BuildApplyComment(projCfg.RepoRelDir, projCfg.Workspace, projCfg.Name),
BaseRepo: ctx.BaseRepo,
EscapedCommentArgs: p.escapeArgs(commentArgs),
AutomergeEnabled: automergeEnabled,
ParallelApplyEnabled: parallelApplyEnabled,
ParallelPlanEnabled: parallelPlanEnabled,
AutoplanEnabled: projCfg.AutoplanEnabled,
Steps: steps,
HeadRepo: ctx.HeadRepo,
Log: ctx.Log,
PullMergeable: ctx.PullMergeable,
Pull: ctx.Pull,
ProjectName: projCfg.Name,
ApplyRequirements: projCfg.ApplyRequirements,
RePlanCmd: p.CommentBuilder.BuildPlanComment(projCfg.RepoRelDir, projCfg.Workspace, projCfg.Name, commentArgs),
RepoRelDir: projCfg.RepoRelDir,
RepoConfigVersion: projCfg.RepoCfgVersion,
TerraformVersion: projCfg.TerraformVersion,
User: ctx.User,
Verbose: verbose,
Workspace: projCfg.Workspace,
ApplyCmd: p.CommentBuilder.BuildApplyComment(projCfg.RepoRelDir, projCfg.Workspace, projCfg.Name),
BaseRepo: ctx.BaseRepo,
EscapedCommentArgs: p.escapeArgs(commentArgs),
AutomergeEnabled: automergeEnabled,
DeleteSourceBranchOnMerge: deleteBranchOnMerge,
ParallelApplyEnabled: parallelApplyEnabled,
ParallelPlanEnabled: parallelPlanEnabled,
AutoplanEnabled: projCfg.AutoplanEnabled,
Steps: steps,
HeadRepo: ctx.HeadRepo,
Log: ctx.Log,
PullMergeable: ctx.PullMergeable,
Pull: ctx.Pull,
ProjectName: projCfg.Name,
ApplyRequirements: projCfg.ApplyRequirements,
RePlanCmd: p.CommentBuilder.BuildPlanComment(projCfg.RepoRelDir, projCfg.Workspace, projCfg.Name, commentArgs),
RepoRelDir: projCfg.RepoRelDir,
RepoConfigVersion: projCfg.RepoCfgVersion,
TerraformVersion: projCfg.TerraformVersion,
User: ctx.User,
Verbose: verbose,
Workspace: projCfg.Workspace,
}
}

Expand Down
2 changes: 1 addition & 1 deletion server/events/vcs/azuredevops_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ func (g *AzureDevopsClient) MergePull(pull models.PullRequest) error {
completionOpts := azuredevops.GitPullRequestCompletionOptions{
BypassPolicy: new(bool),
BypassReason: azuredevops.String(""),
DeleteSourceBranch: new(bool),
DeleteSourceBranch: &pull.DeleteSourceBranchOnMerge,
MergeCommitMessage: azuredevops.String(common.AutomergeCommitMsg),
MergeStrategy: &mcm,
SquashMerge: new(bool),
Expand Down
3 changes: 2 additions & 1 deletion server/events/vcs/gitlab_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ func (g *GitlabClient) MergePull(pull models.PullRequest) error {
pull.BaseRepo.FullName,
pull.Num,
&gitlab.AcceptMergeRequestOptions{
MergeCommitMessage: &commitMsg,
MergeCommitMessage: &commitMsg,
ShouldRemoveSourceBranch: &pull.DeleteSourceBranchOnMerge,
})
return errors.Wrap(err, "unable to merge merge request, it may not be in a mergeable state")
}
Expand Down
34 changes: 22 additions & 12 deletions server/events/yaml/raw/repo_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ const DefaultParallelApply = false
// DefaultParallelPlan is the default setting for parallel plan
const DefaultParallelPlan = false

// DefaultDeleteSourceBranchOnMerge being false is the default setting whether or not to remove a source branch on merge
const DefaultDeleteSourceBranchOnMerge = false

// RepoCfg is the raw schema for repo-level atlantis.yaml config.
type RepoCfg struct {
Version *int `yaml:"version,omitempty"`
Projects []Project `yaml:"projects,omitempty"`
Workflows map[string]Workflow `yaml:"workflows,omitempty"`
Automerge *bool `yaml:"automerge,omitempty"`
ParallelApply *bool `yaml:"parallel_apply,omitempty"`
ParallelPlan *bool `yaml:"parallel_plan,omitempty"`
Version *int `yaml:"version,omitempty"`
Projects []Project `yaml:"projects,omitempty"`
Workflows map[string]Workflow `yaml:"workflows,omitempty"`
Automerge *bool `yaml:"automerge,omitempty"`
ParallelApply *bool `yaml:"parallel_apply,omitempty"`
ParallelPlan *bool `yaml:"parallel_plan,omitempty"`
DeleteSourceBranchOnMerge *bool `yaml:"delete_source_branch_on_merge,omitempty"`
}

func (r RepoCfg) Validate() error {
Expand Down Expand Up @@ -70,12 +74,18 @@ func (r RepoCfg) ToValid() valid.RepoCfg {
parallelPlan = *r.ParallelPlan
}

deleteBranchOnMerge := DefaultDeleteSourceBranchOnMerge
if r.DeleteSourceBranchOnMerge != nil {
deleteBranchOnMerge = *r.DeleteSourceBranchOnMerge
}

return valid.RepoCfg{
Version: *r.Version,
Projects: validProjects,
Workflows: validWorkflows,
Automerge: automerge,
ParallelApply: parallelApply,
ParallelPlan: parallelPlan,
Version: *r.Version,
Projects: validProjects,
Workflows: validWorkflows,
Automerge: automerge,
ParallelApply: parallelApply,
ParallelPlan: parallelPlan,
DeleteSourceBranchOnMerge: deleteBranchOnMerge,
}
}
13 changes: 7 additions & 6 deletions server/events/yaml/valid/repo_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import version "github.com/hashicorp/go-version"
// RepoCfg is the atlantis.yaml config after it's been parsed and validated.
type RepoCfg struct {
// Version is the version of the atlantis YAML file.
Version int
Projects []Project
Workflows map[string]Workflow
Automerge bool
ParallelApply bool
ParallelPlan bool
Version int
Projects []Project
Workflows map[string]Workflow
Automerge bool
ParallelApply bool
ParallelPlan bool
DeleteSourceBranchOnMerge bool
}

func (r RepoCfg) FindProjectsByDirWorkspace(repoRelDir string, workspace string) []Project {
Expand Down
Loading

0 comments on commit 4b9b456

Please sign in to comment.