From 23a58febf9782309ae407ec806ceaa49e68e1aec Mon Sep 17 00:00:00 2001 From: Ramon Vermeulen Date: Wed, 13 May 2026 14:42:02 +0200 Subject: [PATCH] feat(cancel): ability to suppress `atlantis cancel` comments when `--hide-unchanged-plan-comments` is set Signed-off-by: Ramon Vermeulen --- runatlantis.io/docs/server-configuration.md | 2 +- server/events/markdown_renderer.go | 27 ++++- server/events/markdown_renderer_test.go | 114 ++++++++++++++++++ .../events/project_command_pool_executor.go | 10 +- .../templates/multi_project_header.tmpl | 1 + .../events/templates/multi_project_plan.tmpl | 1 + 6 files changed, 145 insertions(+), 10 deletions(-) diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 496e8387f1..c19cd25665 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -989,7 +989,7 @@ atlantis server --hide-unchanged-plan-comments ATLANTIS_HIDE_UNCHANGED_PLAN_COMMENTS=true ``` -Remove no-changes plan comments from the pull request. +Remove no-changes plan comments and cancellation plan comments from the pull request. This is useful when you have many projects and want to keep the pull request clean from useless comments. diff --git a/server/events/markdown_renderer.go b/server/events/markdown_renderer.go index 6863bcfe41..a7de533bb5 100644 --- a/server/events/markdown_renderer.go +++ b/server/events/markdown_renderer.go @@ -7,6 +7,7 @@ package events import ( "bytes" "embed" + "errors" "fmt" "strings" "text/template" @@ -124,12 +125,13 @@ type policyCheckResultsData struct { } type projectResultTmplData struct { - Workspace string - RepoRelDir string - ProjectName string - Rendered string - NoChanges bool - IsSuccessful bool + Workspace string + RepoRelDir string + ProjectName string + Rendered string + NoChanges bool + SuppressComment bool + IsSuccessful bool } // Initialize templates @@ -226,6 +228,11 @@ func (m *MarkdownRenderer) renderProjectResults(ctx *command.Context, results [] ProjectName: result.ProjectName, IsSuccessful: result.IsSuccessful(), } + if common.Command == planCommandTitle && common.HideUnchangedPlanComments && isSuppressedPlanError(result.Error) { + resultData.SuppressComment = true + resultsTmplData = append(resultsTmplData, resultData) + continue + } if result.PlanSuccess != nil { result.PlanSuccess.TerraformOutput = strings.TrimSpace(result.PlanSuccess.TerraformOutput) data := planSuccessData{ @@ -431,3 +438,11 @@ func (m *MarkdownRenderer) renderTemplateTrimSpace(tmpl *template.Template, data } return strings.TrimSpace(buf.String()) } + +func isSuppressedPlanError(err error) bool { + if err == nil { + return false + } + + return errors.Is(err, errOperationCancelled) || err.Error() == operationCancelledErrMsg +} diff --git a/server/events/markdown_renderer_test.go b/server/events/markdown_renderer_test.go index 901b19d8bd..6d83815090 100644 --- a/server/events/markdown_renderer_test.go +++ b/server/events/markdown_renderer_test.go @@ -4377,6 +4377,77 @@ Ran Plan for 3 projects: 3 projects, 0 with changes, 3 with no changes, 0 failed +* :fast_forward: To **apply** all unapplied plans from this Pull Request, comment: + $$$shell + atlantis apply + $$$ +* :put_litter_in_its_place: To **delete** all plans and locks from this Pull Request, comment: + $$$shell + atlantis unlock + $$$ +`, + }, + { + "hide cancelled plan errors when hide unchanged plans is enabled", + command.Plan, + "", + []command.ProjectResult{ + { + Workspace: "workspace", + RepoRelDir: "path", + ProjectCommandOutput: command.ProjectCommandOutput{ + PlanSuccess: &models.PlanSuccess{ + TerraformOutput: "terraform-output", + LockURL: "lock-url", + ApplyCmd: "atlantis apply -d path -w workspace", + RePlanCmd: "atlantis plan -d path -w workspace", + }, + }, + }, + { + Workspace: "workspace", + RepoRelDir: "path2", + ProjectName: "projectname", + ProjectCommandOutput: command.ProjectCommandOutput{ + Error: errors.New("operation cancelled via `atlantis cancel` command"), + }, + }, + { + Workspace: "workspace", + RepoRelDir: "path3", + ProjectName: "projectname2", + ProjectCommandOutput: command.ProjectCommandOutput{ + Error: errors.New("operation cancelled via `atlantis cancel` command"), + }, + }, + }, + models.Github, + ` +Ran Plan for 3 projects: + +1. dir: $path$ workspace: $workspace$ +--- + +### 1. dir: $path$ workspace: $workspace$ +$$$diff +terraform-output +$$$ + +* :arrow_forward: To **apply** this plan, comment: + $$$shell + atlantis apply -d path -w workspace + $$$ +* :put_litter_in_its_place: To **delete** this plan and lock, click [here](lock-url) +* :repeat: To **plan** this project again, comment: + $$$shell + atlantis plan -d path -w workspace + $$$ + +--- +### Plan Summary + +3 projects, 1 with changes, 0 with no changes, 2 failed + * :fast_forward: To **apply** all unapplied plans from this Pull Request, comment: $$$shell atlantis apply @@ -4440,3 +4511,46 @@ Ran Plan for 3 projects: }) } } + +func TestRenderProjectResultsHideUnchangedPlansDoesNotHideNonCancellationErrors(t *testing.T) { + r := events.NewMarkdownRenderer( + false, // gitlabSupportsCommonMark + false, // disableApplyAll + false, // disableApply + false, // disableMarkdownFolding + false, // disableRepoLocking + false, // enableDiffMarkdownFormat + "", // markdownTemplateOverridesDir + "atlantis", // executableName + true, // hideUnchangedPlanComments + false, // quietPolicyChecks + ) + + logger := logging.NewNoopLogger(t).WithHistory() + ctx := &command.Context{ + Log: logger, + Pull: models.PullRequest{ + BaseRepo: models.Repo{ + VCSHost: models.VCSHost{Type: models.Github}, + }, + }, + } + + res := command.Result{ + ProjectResults: []command.ProjectResult{ + { + Workspace: "workspace", + RepoRelDir: "path", + ProjectCommandOutput: command.ProjectCommandOutput{ + Error: errors.New("some plan error"), + }, + }, + }, + } + + cmd := &events.CommentCommand{Name: command.Plan} + rendered := r.Render(ctx, res, cmd) + + Assert(t, strings.Contains(rendered, "some plan error"), "expected non-cancellation plan errors to be rendered") +} + diff --git a/server/events/project_command_pool_executor.go b/server/events/project_command_pool_executor.go index 7d8cddad92..dc5f746e6b 100644 --- a/server/events/project_command_pool_executor.go +++ b/server/events/project_command_pool_executor.go @@ -4,7 +4,7 @@ package events import ( - "fmt" + "errors" "sort" "sync" @@ -15,6 +15,10 @@ import ( type prjCmdRunnerFunc func(ctx command.ProjectContext) command.ProjectCommandOutput +const operationCancelledErrMsg = "operation cancelled via `atlantis cancel` command" + +var errOperationCancelled = errors.New(operationCancelledErrMsg) + func RunOneProjectCmd( runnerFunc prjCmdRunnerFunc, cmd command.ProjectContext, @@ -81,7 +85,7 @@ func runProjectCmdsParallel( results = append(results, command.ProjectResult{ Command: pCmd.CommandName, ProjectCommandOutput: command.ProjectCommandOutput{ - Error: fmt.Errorf("operation cancelled via `atlantis cancel` command"), + Error: errOperationCancelled, }, RepoRelDir: pCmd.RepoRelDir, Workspace: pCmd.Workspace, @@ -227,7 +231,7 @@ func createCancelledResults(remainingGroups [][]command.ProjectContext) []comman cancelledResults = append(cancelledResults, command.ProjectResult{ Command: cmd.CommandName, ProjectCommandOutput: command.ProjectCommandOutput{ - Error: fmt.Errorf("operation cancelled via `atlantis cancel` command"), + Error: errOperationCancelled, }, RepoRelDir: cmd.RepoRelDir, Workspace: cmd.Workspace, diff --git a/server/events/templates/multi_project_header.tmpl b/server/events/templates/multi_project_header.tmpl index c1ce5dc053..8021b2df29 100644 --- a/server/events/templates/multi_project_header.tmpl +++ b/server/events/templates/multi_project_header.tmpl @@ -2,6 +2,7 @@ Ran {{.Command}} for {{ len .Results }} projects: {{ range $result := .Results -}} +{{ if $result.SuppressComment }}{{continue}}{{end -}} 1. {{ if $result.ProjectName }}project: `{{ $result.ProjectName }}` {{ end }}dir: `{{ $result.RepoRelDir }}` workspace: `{{ $result.Workspace }}` {{ end -}} {{ if (gt (len .Results) 0) -}} diff --git a/server/events/templates/multi_project_plan.tmpl b/server/events/templates/multi_project_plan.tmpl index f57e96794a..aa180f3ae7 100644 --- a/server/events/templates/multi_project_plan.tmpl +++ b/server/events/templates/multi_project_plan.tmpl @@ -3,6 +3,7 @@ {{ $disableApplyAll := .DisableApplyAll -}} {{ $hideUnchangedPlans := .HideUnchangedPlanComments -}} {{ range $i, $result := .Results -}} +{{ if $result.SuppressComment }}{{continue}}{{end -}} {{ if (and $hideUnchangedPlans $result.NoChanges) }}{{continue}}{{end -}} ### {{ add $i 1 }}. {{ if $result.ProjectName }}project: `{{ $result.ProjectName }}` {{ end }}dir: `{{ $result.RepoRelDir }}` workspace: `{{ $result.Workspace }}` {{ $result.Rendered }}