diff --git a/server/events/comment_parser.go b/server/events/comment_parser.go index 3b3d2d3b0a..0edc31f2e4 100644 --- a/server/events/comment_parser.go +++ b/server/events/comment_parser.go @@ -31,20 +31,22 @@ import ( ) const ( - workspaceFlagLong = "workspace" - workspaceFlagShort = "w" - dirFlagLong = "dir" - dirFlagShort = "d" - projectFlagLong = "project" - projectFlagShort = "p" - policySetFlagLong = "policy-set" - policySetFlagShort = "" - autoMergeDisabledFlagLong = "auto-merge-disabled" - autoMergeDisabledFlagShort = "" - verboseFlagLong = "verbose" - verboseFlagShort = "" - clearPolicyApprovalFlagLong = "clear-policy-approval" - clearPolicyApprovalFlagShort = "" + workspaceFlagLong = "workspace" + workspaceFlagShort = "w" + dirFlagLong = "dir" + dirFlagShort = "d" + projectFlagLong = "project" + projectFlagShort = "p" + policySetFlagLong = "policy-set" + policySetFlagShort = "" + autoMergeDisabledFlagLong = "auto-merge-disabled" + autoMergeDisabledFlagShort = "" + verboseFlagLong = "verbose" + verboseFlagShort = "" + clearPolicyApprovalFlagLong = "clear-policy-approval" + clearPolicyApprovalFlagShort = "" + allowPartialSuccessVerboseFlagLong = "allow-partial-success" + allowPartialSuccessVerboseFlagShort = "" ) // multiLineRegex is used to ignore multi-line comments since those aren't valid @@ -226,7 +228,7 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com var project string var policySet string var clearPolicyApproval bool - var verbose, autoMergeDisabled bool + var verbose, autoMergeDisabled, allowPartialSuccess bool var flagSet *pflag.FlagSet var name command.Name @@ -240,6 +242,7 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com flagSet.StringVarP(&dir, dirFlagLong, dirFlagShort, "", "Which directory to run plan in relative to root of repo, ex. 'child/dir'.") flagSet.StringVarP(&project, projectFlagLong, projectFlagShort, "", "Which project to run plan for. Refers to the name of the project configured in a repo config file. Cannot be used at same time as workspace or dir flags.") flagSet.BoolVarP(&verbose, verboseFlagLong, verboseFlagShort, false, "Append Atlantis log to comment.") + flagSet.BoolVarP(&allowPartialSuccess, allowPartialSuccessVerboseFlagLong, allowPartialSuccessVerboseFlagLong, false, "Allow for some plans to succeed even when auto merge is enabled (the default is an atomic action which deletes all plans upon failure of at least one other plan).") case command.Apply.String(): name = command.Apply flagSet = pflag.NewFlagSet(command.Apply.String(), pflag.ContinueOnError) @@ -318,7 +321,7 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com } return CommentParseResult{ - Command: NewCommentCommand(dir, extraArgs, name, subName, verbose, autoMergeDisabled, workspace, project, policySet, clearPolicyApproval), + Command: NewCommentCommand(dir, extraArgs, name, subName, verbose, autoMergeDisabled, workspace, project, policySet, clearPolicyApproval, allowPartialSuccess), } } diff --git a/server/events/event_parser.go b/server/events/event_parser.go index 8a02f476e1..8785e6b2c8 100644 --- a/server/events/event_parser.go +++ b/server/events/event_parser.go @@ -141,6 +141,8 @@ type CommentCommand struct { PolicySet string // ClearPolicyApproval is true if approvals should be cleared out for specified policies. ClearPolicyApproval bool + // AllowPartialSuccess is true if plans should be saved even if there were partial failures (only relevant if automerge is true) + AllowPartialSuccess bool } // IsForSpecificProject returns true if the command is for a specific dir, workspace @@ -177,11 +179,11 @@ func (c CommentCommand) IsAutoplan() bool { // String returns a string representation of the command. func (c CommentCommand) String() string { - return fmt.Sprintf("command=%q verbose=%t dir=%q workspace=%q project=%q policyset=%q, clear-policy-approval=%t, flags=%q", c.Name.String(), c.Verbose, c.RepoRelDir, c.Workspace, c.ProjectName, c.PolicySet, c.ClearPolicyApproval, strings.Join(c.Flags, ",")) + return fmt.Sprintf("command=%q verbose=%t dir=%q workspace=%q project=%q policyset=%q, clear-policy-approval=%t, allow-partial-success=%t, flags=%q", c.Name.String(), c.Verbose, c.RepoRelDir, c.Workspace, c.ProjectName, c.PolicySet, c.ClearPolicyApproval, c.AllowPartialSuccess, strings.Join(c.Flags, ",")) } // NewCommentCommand constructs a CommentCommand, setting all missing fields to defaults. -func NewCommentCommand(repoRelDir string, flags []string, name command.Name, subName string, verbose, autoMergeDisabled bool, workspace string, project string, policySet string, clearPolicyApproval bool) *CommentCommand { +func NewCommentCommand(repoRelDir string, flags []string, name command.Name, subName string, verbose, autoMergeDisabled bool, workspace string, project string, policySet string, clearPolicyApproval bool, allowPartialSuccess bool) *CommentCommand { // If repoRelDir was empty we want to keep it that way to indicate that it // wasn't specified in the comment. if repoRelDir != "" { @@ -201,6 +203,7 @@ func NewCommentCommand(repoRelDir string, flags []string, name command.Name, sub ProjectName: project, PolicySet: policySet, ClearPolicyApproval: clearPolicyApproval, + AllowPartialSuccess: allowPartialSuccess, } } diff --git a/server/events/plan_command_runner.go b/server/events/plan_command_runner.go index 85c9f21ec1..45de7d388c 100644 --- a/server/events/plan_command_runner.go +++ b/server/events/plan_command_runner.go @@ -269,7 +269,7 @@ func (p *PlanCommandRunner) run(ctx *command.Context, cmd *CommentCommand) { result = runProjectCmds(projectCmds, p.prjCmdRunner.Plan) } - if p.autoMerger.automergeEnabled(projectCmds) && result.HasErrors() { + if !cmd.AllowPartialSuccess && p.autoMerger.automergeEnabled(projectCmds) && result.HasErrors() { ctx.Log.Info("deleting plans because there were errors and automerge requires all plans succeed") p.deletePlans(ctx) result.PlansDeleted = true