diff --git a/runatlantis.io/docs/repo-level-atlantis-yaml.md b/runatlantis.io/docs/repo-level-atlantis-yaml.md
index 0c9af490d6..11feb31224 100644
--- a/runatlantis.io/docs/repo-level-atlantis-yaml.md
+++ b/runatlantis.io/docs/repo-level-atlantis-yaml.md
@@ -78,6 +78,7 @@ projects:
plan_requirements: [mergeable, approved, undiverged]
apply_requirements: [mergeable, approved, undiverged]
import_requirements: [mergeable, approved, undiverged]
+ silence_pr_comments: ["apply"]
execution_order_group: 1
depends_on:
- project-1
@@ -433,6 +434,7 @@ terraform_version: 0.11.0
plan_requirements: ["approved"]
apply_requirements: ["approved"]
import_requirements: ["approved"]
+silence_pr_comments: ["apply"]
workflow: myworkflow
```
@@ -452,6 +454,7 @@ workflow: myworkflow
| plan_requirements
*(restricted)* | array\[string\] | none | no | Requirements that must be satisfied before `atlantis plan` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.md) for more details. |
| apply_requirements
*(restricted)* | array\[string\] | none | no | Requirements that must be satisfied before `atlantis apply` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.md) for more details. |
| import_requirements
*(restricted)* | array\[string\] | none | no | Requirements that must be satisfied before `atlantis import` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.md) for more details. |
+| silence_pr_comments | array\[string\] | none | no | Silence PR comments from defined stages while preserving PR status checks. Supported values are: `plan`, `apply`. |
| workflow
*(restricted)* | string | none | no | A custom workflow. If not specified, Atlantis will use its default workflow. |
::: tip
diff --git a/runatlantis.io/docs/server-side-repo-config.md b/runatlantis.io/docs/server-side-repo-config.md
index f7cd73595c..16899ac9a3 100644
--- a/runatlantis.io/docs/server-side-repo-config.md
+++ b/runatlantis.io/docs/server-side-repo-config.md
@@ -542,6 +542,7 @@ If you set a workflow with the key `default`, it will override this.
| policy_check | bool | false | no | Whether or not to run policy checks on this repository. |
| custom_policy_check | bool | false | no | Whether or not to enable custom policy check tools outside of Conftest on this repository. |
| autodiscover | AutoDiscover | none | no | Auto discover settings for this repo |
+| silence_pr_comments | []string | none | no | Silence PR comments from defined stages while preserving PR status checks. Useful in large environments with many Atlantis instances and/or projects, when the comments are too big and too many, therefore it is preferable to rely solely on PR status checks. Supported values are: `plan`, `apply`. |
:::tip Notes
diff --git a/server/core/config/parser_validator_test.go b/server/core/config/parser_validator_test.go
index 24b66e4db7..815adbd338 100644
--- a/server/core/config/parser_validator_test.go
+++ b/server/core/config/parser_validator_test.go
@@ -1286,7 +1286,7 @@ func TestParseGlobalCfg(t *testing.T) {
input: `repos:
- id: /.*/
allowed_overrides: [invalid]`,
- expErr: "repos: (0: (allowed_overrides: \"invalid\" is not a valid override, only \"plan_requirements\", \"apply_requirements\", \"import_requirements\", \"workflow\", \"delete_source_branch_on_merge\", \"repo_locking\", \"repo_locks\", \"policy_check\", and \"custom_policy_check\" are supported.).).",
+ expErr: "repos: (0: (allowed_overrides: \"invalid\" is not a valid override, only \"plan_requirements\", \"apply_requirements\", \"import_requirements\", \"workflow\", \"delete_source_branch_on_merge\", \"repo_locking\", \"repo_locks\", \"policy_check\", \"custom_policy_check\", and \"silence_pr_comments\" are supported.).).",
},
"invalid plan_requirement": {
input: `repos:
@@ -1306,8 +1306,14 @@ func TestParseGlobalCfg(t *testing.T) {
import_requirements: [invalid]`,
expErr: "repos: (0: (import_requirements: \"invalid\" is not a valid import_requirement, only \"approved\", \"mergeable\" and \"undiverged\" are supported.).).",
},
+ "invalid silence_pr_comments": {
+ input: `repos:
+- id: /.*/
+ silence_pr_comments: [invalid]`,
+ expErr: "server-side repo config 'silence_pr_comments' key value of 'invalid' is not supported, supported values are [plan, apply]",
+ },
"disable autodiscover": {
- input: `repos:
+ input: `repos:
- id: /.*/
autodiscover:
mode: disabled`,
diff --git a/server/core/config/raw/global_cfg.go b/server/core/config/raw/global_cfg.go
index 445332ec39..0c9b2c351d 100644
--- a/server/core/config/raw/global_cfg.go
+++ b/server/core/config/raw/global_cfg.go
@@ -8,6 +8,7 @@ import (
validation "github.com/go-ozzo/ozzo-validation"
"github.com/pkg/errors"
"github.com/runatlantis/atlantis/server/core/config/valid"
+ "github.com/runatlantis/atlantis/server/utils"
)
// GlobalCfg is the raw schema for server-side repo config.
@@ -38,6 +39,7 @@ type Repo struct {
PolicyCheck *bool `yaml:"policy_check,omitempty" json:"policy_check,omitempty"`
CustomPolicyCheck *bool `yaml:"custom_policy_check,omitempty" json:"custom_policy_check,omitempty"`
AutoDiscover *AutoDiscover `yaml:"autodiscover,omitempty" json:"autodiscover,omitempty"`
+ SilencePRComments []string `yaml:"silence_pr_comments,omitempty" json:"silence_pr_comments,omitempty"`
}
func (g GlobalCfg) Validate() error {
@@ -94,6 +96,24 @@ func (g GlobalCfg) Validate() error {
}
}
}
+
+ // Validate supported SilencePRComments values.
+ for _, repo := range g.Repos {
+ if repo.SilencePRComments == nil {
+ continue
+ }
+ for _, silenceStage := range repo.SilencePRComments {
+ if !utils.SlicesContains(valid.AllowedSilencePRComments, silenceStage) {
+ return fmt.Errorf(
+ "server-side repo config '%s' key value of '%s' is not supported, supported values are [%s]",
+ valid.SilencePRCommentsKey,
+ silenceStage,
+ strings.Join(valid.AllowedSilencePRComments, ", "),
+ )
+ }
+ }
+ }
+
return nil
}
@@ -195,8 +215,8 @@ func (r Repo) Validate() error {
overridesValid := func(value interface{}) error {
overrides := value.([]string)
for _, o := range overrides {
- if o != valid.PlanRequirementsKey && o != valid.ApplyRequirementsKey && o != valid.ImportRequirementsKey && o != valid.WorkflowKey && o != valid.DeleteSourceBranchOnMergeKey && o != valid.RepoLockingKey && o != valid.RepoLocksKey && o != valid.PolicyCheckKey && o != valid.CustomPolicyCheckKey {
- return fmt.Errorf("%q is not a valid override, only %q, %q, %q, %q, %q, %q, %q, %q, and %q are supported", o, valid.PlanRequirementsKey, valid.ApplyRequirementsKey, valid.ImportRequirementsKey, valid.WorkflowKey, valid.DeleteSourceBranchOnMergeKey, valid.RepoLockingKey, valid.RepoLocksKey, valid.PolicyCheckKey, valid.CustomPolicyCheckKey)
+ if o != valid.PlanRequirementsKey && o != valid.ApplyRequirementsKey && o != valid.ImportRequirementsKey && o != valid.WorkflowKey && o != valid.DeleteSourceBranchOnMergeKey && o != valid.RepoLockingKey && o != valid.RepoLocksKey && o != valid.PolicyCheckKey && o != valid.CustomPolicyCheckKey && o != valid.SilencePRCommentsKey {
+ return fmt.Errorf("%q is not a valid override, only %q, %q, %q, %q, %q, %q, %q, %q, %q, and %q are supported", o, valid.PlanRequirementsKey, valid.ApplyRequirementsKey, valid.ImportRequirementsKey, valid.WorkflowKey, valid.DeleteSourceBranchOnMergeKey, valid.RepoLockingKey, valid.RepoLocksKey, valid.PolicyCheckKey, valid.CustomPolicyCheckKey, valid.SilencePRCommentsKey)
}
}
return nil
@@ -365,5 +385,6 @@ OuterGlobalImportReqs:
PolicyCheck: r.PolicyCheck,
CustomPolicyCheck: r.CustomPolicyCheck,
AutoDiscover: autoDiscover,
+ SilencePRComments: r.SilencePRComments,
}
}
diff --git a/server/core/config/raw/project.go b/server/core/config/raw/project.go
index a20aad10af..fe0e656a8c 100644
--- a/server/core/config/raw/project.go
+++ b/server/core/config/raw/project.go
@@ -38,6 +38,7 @@ type Project struct {
ExecutionOrderGroup *int `yaml:"execution_order_group,omitempty"`
PolicyCheck *bool `yaml:"policy_check,omitempty"`
CustomPolicyCheck *bool `yaml:"custom_policy_check,omitempty"`
+ SilencePRComments []string `yaml:"silence_pr_comments,omitempty"`
}
func (p Project) Validate() error {
@@ -156,6 +157,10 @@ func (p Project) ToValid() valid.Project {
v.CustomPolicyCheck = p.CustomPolicyCheck
}
+ if p.SilencePRComments != nil {
+ v.SilencePRComments = p.SilencePRComments
+ }
+
return v
}
diff --git a/server/core/config/raw/repo_cfg.go b/server/core/config/raw/repo_cfg.go
index e7b4b273ab..9aa18c7733 100644
--- a/server/core/config/raw/repo_cfg.go
+++ b/server/core/config/raw/repo_cfg.go
@@ -28,6 +28,7 @@ type RepoCfg struct {
AllowedRegexpPrefixes []string `yaml:"allowed_regexp_prefixes,omitempty"`
AbortOnExcecutionOrderFail *bool `yaml:"abort_on_execution_order_fail,omitempty"`
RepoLocks *RepoLocks `yaml:"repo_locks,omitempty"`
+ SilencePRComments []string `yaml:"silence_pr_comments,omitempty"`
}
func (r RepoCfg) Validate() error {
@@ -96,5 +97,6 @@ func (r RepoCfg) ToValid() valid.RepoCfg {
EmojiReaction: emojiReaction,
AbortOnExcecutionOrderFail: abortOnExcecutionOrderFail,
RepoLocks: repoLocks,
+ SilencePRComments: r.SilencePRComments,
}
}
diff --git a/server/core/config/valid/global_cfg.go b/server/core/config/valid/global_cfg.go
index 72bfa60432..ac41b3865e 100644
--- a/server/core/config/valid/global_cfg.go
+++ b/server/core/config/valid/global_cfg.go
@@ -27,6 +27,9 @@ const RepoLocksKey = "repo_locks"
const PolicyCheckKey = "policy_check"
const CustomPolicyCheckKey = "custom_policy_check"
const AutoDiscoverKey = "autodiscover"
+const SilencePRCommentsKey = "silence_pr_comments"
+
+var AllowedSilencePRComments = []string{"plan", "apply"}
// DefaultAtlantisFile is the default name of the config file for each repo.
const DefaultAtlantisFile = "atlantis.yaml"
@@ -85,6 +88,7 @@ type Repo struct {
PolicyCheck *bool
CustomPolicyCheck *bool
AutoDiscover *AutoDiscover
+ SilencePRComments []string
}
type MergedProjectCfg struct {
@@ -107,6 +111,7 @@ type MergedProjectCfg struct {
RepoLocks RepoLocks
PolicyCheck bool
CustomPolicyCheck bool
+ SilencePRComments []string
}
// WorkflowHook is a map of custom run commands to run before or after workflows.
@@ -212,8 +217,9 @@ func NewGlobalCfgFromArgs(args GlobalCfgArgs) GlobalCfg {
repoLocks := DefaultRepoLocks
customPolicyCheck := false
autoDiscover := AutoDiscover{Mode: AutoDiscoverAutoMode}
+ var silencePRComments []string
if args.AllowAllRepoSettings {
- allowedOverrides = []string{PlanRequirementsKey, ApplyRequirementsKey, ImportRequirementsKey, WorkflowKey, DeleteSourceBranchOnMergeKey, RepoLockingKey, RepoLocksKey, PolicyCheckKey}
+ allowedOverrides = []string{PlanRequirementsKey, ApplyRequirementsKey, ImportRequirementsKey, WorkflowKey, DeleteSourceBranchOnMergeKey, RepoLockingKey, RepoLocksKey, PolicyCheckKey, SilencePRCommentsKey}
allowCustomWorkflows = true
}
@@ -237,6 +243,7 @@ func NewGlobalCfgFromArgs(args GlobalCfgArgs) GlobalCfg {
PolicyCheck: &policyCheck,
CustomPolicyCheck: &customPolicyCheck,
AutoDiscover: &autoDiscover,
+ SilencePRComments: silencePRComments,
},
},
Workflows: map[string]Workflow{
@@ -273,7 +280,7 @@ func (r Repo) IDString() string {
// final config. It assumes that all configs have been validated.
func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, proj Project, rCfg RepoCfg) MergedProjectCfg {
log.Debug("MergeProjectCfg started")
- planReqs, applyReqs, importReqs, workflow, allowedOverrides, allowCustomWorkflows, deleteSourceBranchOnMerge, repoLocks, policyCheck, customPolicyCheck, _ := g.getMatchingCfg(log, repoID)
+ planReqs, applyReqs, importReqs, workflow, allowedOverrides, allowCustomWorkflows, deleteSourceBranchOnMerge, repoLocks, policyCheck, customPolicyCheck, _, silencePRComments := g.getMatchingCfg(log, repoID)
// If repos are allowed to override certain keys then override them.
for _, key := range allowedOverrides {
switch key {
@@ -366,12 +373,29 @@ func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, pro
log.Debug("overriding server-defined %s with repo settings: [%t]", CustomPolicyCheckKey, *proj.CustomPolicyCheck)
customPolicyCheck = *proj.CustomPolicyCheck
}
+ case SilencePRCommentsKey:
+ if proj.SilencePRComments != nil {
+ log.Debug("overriding repo-root-defined %s with repo settings: [%t]", SilencePRCommentsKey, strings.Join(proj.SilencePRComments, ","))
+ silencePRComments = proj.SilencePRComments
+ } else if rCfg.SilencePRComments != nil {
+ log.Debug("overriding server-defined %s with repo settings: [%s]", SilencePRCommentsKey, strings.Join(rCfg.SilencePRComments, ","))
+ silencePRComments = rCfg.SilencePRComments
+ }
}
log.Debug("MergeProjectCfg completed")
}
- log.Debug("final settings: %s: [%s], %s: [%s], %s: [%s], %s: %s",
- PlanRequirementsKey, strings.Join(planReqs, ","), ApplyRequirementsKey, strings.Join(applyReqs, ","), ImportRequirementsKey, strings.Join(importReqs, ","), WorkflowKey, workflow.Name)
+ log.Debug("final settings: %s: [%s], %s: [%s], %s: [%s], %s: %s, %s: %t, %s: %s, %s: %t, %s: %t, %s: [%s]",
+ PlanRequirementsKey, strings.Join(planReqs, ","),
+ ApplyRequirementsKey, strings.Join(applyReqs, ","),
+ ImportRequirementsKey, strings.Join(importReqs, ","),
+ WorkflowKey, workflow.Name,
+ DeleteSourceBranchOnMergeKey, deleteSourceBranchOnMerge,
+ RepoLockingKey, repoLocks.Mode,
+ PolicyCheckKey, policyCheck,
+ CustomPolicyCheckKey, policyCheck,
+ SilencePRCommentsKey, strings.Join(silencePRComments, ","),
+ )
return MergedProjectCfg{
PlanRequirements: planReqs,
@@ -391,6 +415,7 @@ func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, pro
RepoLocks: repoLocks,
PolicyCheck: policyCheck,
CustomPolicyCheck: customPolicyCheck,
+ SilencePRComments: silencePRComments,
}
}
@@ -398,7 +423,7 @@ func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, pro
// repo with id repoID. It is used when there is no repo config.
func (g GlobalCfg) DefaultProjCfg(log logging.SimpleLogging, repoID string, repoRelDir string, workspace string) MergedProjectCfg {
log.Debug("building config based on server-side config")
- planReqs, applyReqs, importReqs, workflow, _, _, deleteSourceBranchOnMerge, repoLocks, policyCheck, customPolicyCheck, _ := g.getMatchingCfg(log, repoID)
+ planReqs, applyReqs, importReqs, workflow, _, _, deleteSourceBranchOnMerge, repoLocks, policyCheck, customPolicyCheck, _, silencePRComments := g.getMatchingCfg(log, repoID)
return MergedProjectCfg{
PlanRequirements: planReqs,
ApplyRequirements: applyReqs,
@@ -414,6 +439,7 @@ func (g GlobalCfg) DefaultProjCfg(log logging.SimpleLogging, repoID string, repo
RepoLocks: repoLocks,
PolicyCheck: policyCheck,
CustomPolicyCheck: customPolicyCheck,
+ SilencePRComments: silencePRComments,
}
}
@@ -474,6 +500,26 @@ func (g GlobalCfg) ValidateRepoCfg(rCfg RepoCfg, repoID string) error {
if p.CustomPolicyCheck != nil && !utils.SlicesContains(allowedOverrides, CustomPolicyCheckKey) {
return fmt.Errorf("repo config not allowed to set '%s' key: server-side config needs '%s: [%s]'", CustomPolicyCheckKey, AllowedOverridesKey, CustomPolicyCheckKey)
}
+ if p.SilencePRComments != nil {
+ if !utils.SlicesContains(allowedOverrides, SilencePRCommentsKey) {
+ return fmt.Errorf(
+ "repo config not allowed to set '%s' key: server-side config needs '%s: [%s]'",
+ SilencePRCommentsKey,
+ AllowedOverridesKey,
+ SilencePRCommentsKey,
+ )
+ }
+ for _, silenceStage := range p.SilencePRComments {
+ if !utils.SlicesContains(AllowedSilencePRComments, silenceStage) {
+ return fmt.Errorf(
+ "repo config '%s' key value of '%s' is not supported, supported values are [%s]",
+ SilencePRCommentsKey,
+ silenceStage,
+ strings.Join(AllowedSilencePRComments, ", "),
+ )
+ }
+ }
+ }
}
// Check custom workflows.
@@ -532,7 +578,7 @@ func (g GlobalCfg) ValidateRepoCfg(rCfg RepoCfg, repoID string) error {
}
// getMatchingCfg returns the key settings for repoID.
-func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (planReqs []string, applyReqs []string, importReqs []string, workflow Workflow, allowedOverrides []string, allowCustomWorkflows bool, deleteSourceBranchOnMerge bool, repoLocks RepoLocks, policyCheck bool, customPolicyCheck bool, autoDiscover AutoDiscover) {
+func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (planReqs []string, applyReqs []string, importReqs []string, workflow Workflow, allowedOverrides []string, allowCustomWorkflows bool, deleteSourceBranchOnMerge bool, repoLocks RepoLocks, policyCheck bool, customPolicyCheck bool, autoDiscover AutoDiscover, silencePRComments []string) {
toLog := make(map[string]string)
traceF := func(repoIdx int, repoID string, key string, val interface{}) string {
from := "default server config"
@@ -559,7 +605,7 @@ func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (pla
repoLocking := true
repoLocks = DefaultRepoLocks
- for _, key := range []string{PlanRequirementsKey, ApplyRequirementsKey, ImportRequirementsKey, WorkflowKey, AllowedOverridesKey, AllowCustomWorkflowsKey, DeleteSourceBranchOnMergeKey, RepoLockingKey, RepoLocksKey, PolicyCheckKey, CustomPolicyCheckKey} {
+ for _, key := range []string{PlanRequirementsKey, ApplyRequirementsKey, ImportRequirementsKey, WorkflowKey, AllowedOverridesKey, AllowCustomWorkflowsKey, DeleteSourceBranchOnMergeKey, RepoLockingKey, RepoLocksKey, PolicyCheckKey, CustomPolicyCheckKey, SilencePRCommentsKey} {
for i, repo := range g.Repos {
if repo.IDMatches(repoID) {
switch key {
@@ -623,6 +669,11 @@ func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (pla
toLog[AutoDiscoverKey] = traceF(i, repo.IDString(), AutoDiscoverKey, repo.AutoDiscover.Mode)
autoDiscover = *repo.AutoDiscover
}
+ case SilencePRCommentsKey:
+ if repo.SilencePRComments != nil {
+ toLog[SilencePRCommentsKey] = traceF(i, repo.IDString(), SilencePRCommentsKey, repo.SilencePRComments)
+ silencePRComments = repo.SilencePRComments
+ }
}
}
}
diff --git a/server/core/config/valid/global_cfg_test.go b/server/core/config/valid/global_cfg_test.go
index 6c1b94ded2..129b41d462 100644
--- a/server/core/config/valid/global_cfg_test.go
+++ b/server/core/config/valid/global_cfg_test.go
@@ -129,7 +129,7 @@ func TestNewGlobalCfg(t *testing.T) {
if c.allowAllRepoSettings {
exp.Repos[0].AllowCustomWorkflows = Bool(true)
- exp.Repos[0].AllowedOverrides = []string{"plan_requirements", "apply_requirements", "import_requirements", "workflow", "delete_source_branch_on_merge", "repo_locking", "repo_locks", "policy_check"}
+ exp.Repos[0].AllowedOverrides = []string{"plan_requirements", "apply_requirements", "import_requirements", "workflow", "delete_source_branch_on_merge", "repo_locking", "repo_locks", "policy_check", "silence_pr_comments"}
}
if c.policyCheckEnabled {
exp.Repos[0].PlanRequirements = append(exp.Repos[0].PlanRequirements, "policies_passed")
diff --git a/server/core/config/valid/repo_cfg.go b/server/core/config/valid/repo_cfg.go
index 7010d1c95d..e5a8378bd7 100644
--- a/server/core/config/valid/repo_cfg.go
+++ b/server/core/config/valid/repo_cfg.go
@@ -29,6 +29,7 @@ type RepoCfg struct {
EmojiReaction string
AllowedRegexpPrefixes []string
AbortOnExcecutionOrderFail bool
+ SilencePRComments []string
}
func (r RepoCfg) FindProjectsByDirWorkspace(repoRelDir string, workspace string) []Project {
@@ -158,6 +159,7 @@ type Project struct {
ExecutionOrderGroup int
PolicyCheck *bool
CustomPolicyCheck *bool
+ SilencePRComments []string
}
// GetName returns the name of the project or an empty string if there is no
diff --git a/server/events/apply_command_runner.go b/server/events/apply_command_runner.go
index 2a40c454d9..ee6bf8ab1f 100644
--- a/server/events/apply_command_runner.go
+++ b/server/events/apply_command_runner.go
@@ -60,6 +60,7 @@ type ApplyCommandRunner struct {
// SilenceVCSStatusNoPlans is whether any plan should set commit status if no projects
// are found
silenceVCSStatusNoProjects bool
+ SilencePRComments []string
}
func (a *ApplyCommandRunner) Run(ctx *command.Context, cmd *CommentCommand) {
diff --git a/server/events/command/project_context.go b/server/events/command/project_context.go
index dc4c6dffa4..6e221ff4d1 100644
--- a/server/events/command/project_context.go
+++ b/server/events/command/project_context.go
@@ -125,6 +125,7 @@ type ProjectContext struct {
AbortOnExcecutionOrderFail bool
// Allows custom policy check tools outside of Conftest to run in checks
CustomPolicyCheck bool
+ SilencePRComments []string
}
// SetProjectScopeTags adds ProjectContext tags to a new returned scope.
diff --git a/server/events/command/project_result.go b/server/events/command/project_result.go
index 0d59c4e9ab..8f72f1d168 100644
--- a/server/events/command/project_result.go
+++ b/server/events/command/project_result.go
@@ -19,6 +19,7 @@ type ProjectResult struct {
ImportSuccess *models.ImportSuccess
StateRmSuccess *models.StateRmSuccess
ProjectName string
+ SilencePRComments []string
}
// CommitStatus returns the vcs commit status of this project result.
diff --git a/server/events/plan_command_runner.go b/server/events/plan_command_runner.go
index b8657f5a85..c2b6b7a107 100644
--- a/server/events/plan_command_runner.go
+++ b/server/events/plan_command_runner.go
@@ -76,6 +76,7 @@ type PlanCommandRunner struct {
// a plan.
DiscardApprovalOnPlan bool
pullReqStatusFetcher vcs.PullReqStatusFetcher
+ SilencePRComments []string
}
func (p *PlanCommandRunner) runAutoplan(ctx *command.Context) {
diff --git a/server/events/project_command_builder.go b/server/events/project_command_builder.go
index d40ae25893..538c71a73f 100644
--- a/server/events/project_command_builder.go
+++ b/server/events/project_command_builder.go
@@ -276,8 +276,7 @@ func (p *DefaultProjectCommandBuilder) BuildPlanCommands(ctx *command.Context, c
}
ctx.Log.Debug("Building plan command for specific project with directory: '%v', workspace: '%v', project: '%v'",
cmd.RepoRelDir, cmd.Workspace, cmd.ProjectName)
- pcc, err := p.buildProjectPlanCommand(ctx, cmd)
- return pcc, err
+ return p.buildProjectPlanCommand(ctx, cmd)
}
// See ProjectCommandBuilder.BuildApplyCommands.
@@ -285,24 +284,21 @@ func (p *DefaultProjectCommandBuilder) BuildApplyCommands(ctx *command.Context,
if !cmd.IsForSpecificProject() {
return p.buildAllProjectCommandsByPlan(ctx, cmd)
}
- pac, err := p.buildProjectCommand(ctx, cmd)
- return pac, err
+ return p.buildProjectCommand(ctx, cmd)
}
func (p *DefaultProjectCommandBuilder) BuildApprovePoliciesCommands(ctx *command.Context, cmd *CommentCommand) ([]command.ProjectContext, error) {
if !cmd.IsForSpecificProject() {
return p.buildAllProjectCommandsByPlan(ctx, cmd)
}
- pac, err := p.buildProjectCommand(ctx, cmd)
- return pac, err
+ return p.buildProjectCommand(ctx, cmd)
}
func (p *DefaultProjectCommandBuilder) BuildVersionCommands(ctx *command.Context, cmd *CommentCommand) ([]command.ProjectContext, error) {
if !cmd.IsForSpecificProject() {
return p.buildAllProjectCommandsByPlan(ctx, cmd)
}
- pac, err := p.buildProjectCommand(ctx, cmd)
- return pac, err
+ return p.buildProjectCommand(ctx, cmd)
}
func (p *DefaultProjectCommandBuilder) BuildImportCommands(ctx *command.Context, cmd *CommentCommand) ([]command.ProjectContext, error) {
diff --git a/server/events/project_command_context_builder.go b/server/events/project_command_context_builder.go
index ea17e02e07..6a08b16471 100644
--- a/server/events/project_command_context_builder.go
+++ b/server/events/project_command_context_builder.go
@@ -306,6 +306,7 @@ func newProjectCommandContext(ctx *command.Context,
JobID: uuid.New().String(),
ExecutionOrderGroup: projCfg.ExecutionOrderGroup,
AbortOnExcecutionOrderFail: abortOnExcecutionOrderFail,
+ SilencePRComments: projCfg.SilencePRComments,
}
}
diff --git a/server/events/project_command_runner.go b/server/events/project_command_runner.go
index cd1b2e0d15..5410dee8cb 100644
--- a/server/events/project_command_runner.go
+++ b/server/events/project_command_runner.go
@@ -225,13 +225,14 @@ type DefaultProjectCommandRunner struct {
func (p *DefaultProjectCommandRunner) Plan(ctx command.ProjectContext) command.ProjectResult {
planSuccess, failure, err := p.doPlan(ctx)
return command.ProjectResult{
- Command: command.Plan,
- PlanSuccess: planSuccess,
- Error: err,
- Failure: failure,
- RepoRelDir: ctx.RepoRelDir,
- Workspace: ctx.Workspace,
- ProjectName: ctx.ProjectName,
+ Command: command.Plan,
+ PlanSuccess: planSuccess,
+ Error: err,
+ Failure: failure,
+ RepoRelDir: ctx.RepoRelDir,
+ Workspace: ctx.Workspace,
+ ProjectName: ctx.ProjectName,
+ SilencePRComments: ctx.SilencePRComments,
}
}
@@ -253,13 +254,14 @@ func (p *DefaultProjectCommandRunner) PolicyCheck(ctx command.ProjectContext) co
func (p *DefaultProjectCommandRunner) Apply(ctx command.ProjectContext) command.ProjectResult {
applyOut, failure, err := p.doApply(ctx)
return command.ProjectResult{
- Command: command.Apply,
- Failure: failure,
- Error: err,
- ApplySuccess: applyOut,
- RepoRelDir: ctx.RepoRelDir,
- Workspace: ctx.Workspace,
- ProjectName: ctx.ProjectName,
+ Command: command.Apply,
+ Failure: failure,
+ Error: err,
+ ApplySuccess: applyOut,
+ RepoRelDir: ctx.RepoRelDir,
+ Workspace: ctx.Workspace,
+ ProjectName: ctx.ProjectName,
+ SilencePRComments: ctx.SilencePRComments,
}
}
diff --git a/server/events/pull_updater.go b/server/events/pull_updater.go
index 2fd2b99a16..d85bd84f9d 100644
--- a/server/events/pull_updater.go
+++ b/server/events/pull_updater.go
@@ -3,6 +3,7 @@ package events
import (
"github.com/runatlantis/atlantis/server/events/command"
"github.com/runatlantis/atlantis/server/events/vcs"
+ "github.com/runatlantis/atlantis/server/utils"
)
type PullUpdater struct {
@@ -23,12 +24,29 @@ func (c *PullUpdater) updatePull(ctx *command.Context, cmd PullCommand, res comm
// clutter in a pull/merge request. This will not delete the comment, since the
// comment trail may be useful in auditing or backtracing problems.
if c.HidePrevPlanComments {
- ctx.Log.Debug("Hiding previous plan comments for command: '%v', directory: '%v'", cmd.CommandName().TitleString(), cmd.Dir())
+ ctx.Log.Debug("hiding previous plan comments for command: '%v', directory: '%v'", cmd.CommandName().TitleString(), cmd.Dir())
if err := c.VCSClient.HidePrevCommandComments(ctx.Log, ctx.Pull.BaseRepo, ctx.Pull.Num, cmd.CommandName().TitleString(), cmd.Dir()); err != nil {
ctx.Log.Err("unable to hide old comments: %s", err)
}
}
+ if len(res.ProjectResults) > 0 {
+ var commentOnProjects []command.ProjectResult
+ for _, result := range res.ProjectResults {
+ if utils.SlicesContains(result.SilencePRComments, cmd.CommandName().String()) {
+ ctx.Log.Debug("silenced command '%s' comment for project '%s'", cmd.CommandName().String(), result.ProjectName)
+ continue
+ }
+ commentOnProjects = append(commentOnProjects, result)
+ }
+
+ if len(commentOnProjects) == 0 {
+ return
+ }
+
+ res.ProjectResults = commentOnProjects
+ }
+
comment := c.MarkdownRenderer.Render(ctx, res, cmd)
if err := c.VCSClient.CreateComment(ctx.Log, ctx.Pull.BaseRepo, ctx.Pull.Num, comment, cmd.CommandName().String()); err != nil {
ctx.Log.Err("unable to comment: %s", err)