diff --git a/cmd/server.go b/cmd/server.go index 74f2348687..3aaab408b1 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -68,6 +68,7 @@ const ( RepoWhitelistFlag = "repo-whitelist" RequireApprovalFlag = "require-approval" RequireMergeableFlag = "require-mergeable" + SilenceForkPRErrorsFlag = "silence-fork-pr-errors" SilenceWhitelistErrorsFlag = "silence-whitelist-errors" SlackTokenFlag = "slack-token" SSLCertFileFlag = "ssl-cert-file" @@ -247,6 +248,10 @@ var boolFlags = map[string]boolFlag{ defaultValue: false, hidden: true, }, + SilenceForkPRErrorsFlag: { + description: "Silences the posting of fork pull requests not allowed error comments.", + defaultValue: false, + }, SilenceWhitelistErrorsFlag: { description: "Silences the posting of whitelist error comments.", defaultValue: false, @@ -420,11 +425,12 @@ func (s *ServerCmd) run() error { // Config looks good. Start the server. server, err := s.ServerCreator.NewServer(userConfig, server.Config{ - AllowForkPRsFlag: AllowForkPRsFlag, - AtlantisURLFlag: AtlantisURLFlag, - AtlantisVersion: s.AtlantisVersion, - DefaultTFVersionFlag: DefaultTFVersionFlag, - RepoConfigJSONFlag: RepoConfigJSONFlag, + AllowForkPRsFlag: AllowForkPRsFlag, + AtlantisURLFlag: AtlantisURLFlag, + AtlantisVersion: s.AtlantisVersion, + DefaultTFVersionFlag: DefaultTFVersionFlag, + RepoConfigJSONFlag: RepoConfigJSONFlag, + SilenceForkPRErrorsFlag: SilenceForkPRErrorsFlag, }) if err != nil { return errors.Wrap(err, "initializing server") diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index a48fd87ee4..e30ad8eff1 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -389,6 +389,13 @@ Values are chosen in this order: ``` Or use `--repo-config-json='{"repos":[{"id":"/.*/", "apply_requirements":["mergeable"]}]}'` instead. +* ### `--silence-fork-pr-errors` + ```bash + atlantis server --silence-fork-pr-errors + ``` + Normally, if Atlantis receives a pull request webhook from a fork and --allow-fork-prs is not set, + it will comment back with an error. This flag disables that commenting. + * ### `--silence-whitelist-errors` ```bash atlantis server --silence-whitelist-errors diff --git a/server/events/command_runner.go b/server/events/command_runner.go index 65e21fe743..cb8b3615dd 100644 --- a/server/events/command_runner.go +++ b/server/events/command_runner.go @@ -78,9 +78,15 @@ type DefaultCommandRunner struct { // AllowForkPRsFlag is the name of the flag that controls fork PR's. We use // this in our error message back to the user on a forked PR so they know // how to enable this functionality. - AllowForkPRsFlag string - ProjectCommandBuilder ProjectCommandBuilder - ProjectCommandRunner ProjectCommandRunner + AllowForkPRsFlag string + // SilenceForkPRErrors controls whether to comment on Fork PRs when AllowForkPRs = False + SilenceForkPRErrors bool + // SilenceForkPRErrorsFlag is the name of the flag that controls fork PR's. We use + // this in our error message back to the user on a forked PR so they know + // how to disable error comment + SilenceForkPRErrorsFlag string + ProjectCommandBuilder ProjectCommandBuilder + ProjectCommandRunner ProjectCommandRunner // GlobalAutomerge is true if we should automatically merge pull requests if all // plans have been successfully applied. This is set via a CLI flag. GlobalAutomerge bool @@ -387,8 +393,11 @@ func (c *DefaultCommandRunner) buildLogger(repoFullName string, pullNum int) *lo func (c *DefaultCommandRunner) validateCtxAndComment(ctx *CommandContext) bool { if !c.AllowForkPRs && ctx.HeadRepo.Owner != ctx.BaseRepo.Owner { + if c.SilenceForkPRErrors { + return false + } ctx.Log.Info("command was run on a fork pull request which is disallowed") - if err := c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, fmt.Sprintf("Atlantis commands can't be run on fork pull requests. To enable, set --%s", c.AllowForkPRsFlag)); err != nil { + if err := c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, fmt.Sprintf("Atlantis commands can't be run on fork pull requests. To enable, set --%s or, to disable this message, set --%s", c.AllowForkPRsFlag, c.SilenceForkPRErrorsFlag)); err != nil { ctx.Log.Err("unable to comment: %s", err) } return false diff --git a/server/events/command_runner_test.go b/server/events/command_runner_test.go index 261184731f..0f6d9b7177 100644 --- a/server/events/command_runner_test.go +++ b/server/events/command_runner_test.go @@ -136,7 +136,28 @@ func TestRunCommentCommand_ForkPRDisabled(t *testing.T) { t.Log("if a command is run on a forked pull request and this is disabled atlantis should" + " comment saying that this is not allowed") vcsClient := setup(t) + // by default these are false so don't need to reset + ch.AllowForkPRs = false + ch.SilenceForkPRErrors = false + var pull github.PullRequest + modelPull := models.PullRequest{State: models.OpenPullState} + When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil) + + headRepo := fixtures.GithubRepo + headRepo.FullName = "forkrepo/atlantis" + headRepo.Owner = "forkrepo" + When(eventParsing.ParseGithubPull(&pull)).ThenReturn(modelPull, modelPull.BaseRepo, headRepo, nil) + + ch.RunCommentCommand(fixtures.GithubRepo, nil, nil, fixtures.User, fixtures.Pull.Num, nil) + commentMessage := fmt.Sprintf("Atlantis commands can't be run on fork pull requests. To enable, set --%s or, to disable this message, set --%s", ch.AllowForkPRsFlag, ch.SilenceForkPRErrorsFlag) + vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, modelPull.Num, commentMessage) +} + +func TestRunCommentCommand_ForkPRDisabled_SilenceEnabled(t *testing.T) { + t.Log("if a command is run on a forked pull request and forks are disabled and we are silencing errors do not comment with error") + vcsClient := setup(t) ch.AllowForkPRs = false // by default it's false so don't need to reset + ch.SilenceForkPRErrors = true var pull github.PullRequest modelPull := models.PullRequest{State: models.OpenPullState} When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil) @@ -147,7 +168,7 @@ func TestRunCommentCommand_ForkPRDisabled(t *testing.T) { When(eventParsing.ParseGithubPull(&pull)).ThenReturn(modelPull, modelPull.BaseRepo, headRepo, nil) ch.RunCommentCommand(fixtures.GithubRepo, nil, nil, fixtures.User, fixtures.Pull.Num, nil) - vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, modelPull.Num, "Atlantis commands can't be run on fork pull requests. To enable, set --"+ch.AllowForkPRsFlag) + vcsClient.VerifyWasCalled(Never()).CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString()) } func TestRunCommentCommand_DisableApplyAllDisabled(t *testing.T) { diff --git a/server/server.go b/server/server.go index 2e1c584bfd..aaa657c3d7 100644 --- a/server/server.go +++ b/server/server.go @@ -83,11 +83,12 @@ type Server struct { // Config holds config for server that isn't passed in by the user. type Config struct { - AllowForkPRsFlag string - AtlantisURLFlag string - AtlantisVersion string - DefaultTFVersionFlag string - RepoConfigJSONFlag string + AllowForkPRsFlag string + AtlantisURLFlag string + AtlantisVersion string + DefaultTFVersionFlag string + RepoConfigJSONFlag string + SilenceForkPRErrorsFlag string } // WebhookConfig is nested within UserConfig. It's used to configure webhooks. @@ -312,6 +313,8 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { Logger: logger, AllowForkPRs: userConfig.AllowForkPRs, AllowForkPRsFlag: config.AllowForkPRsFlag, + SilenceForkPRErrors: userConfig.SilenceForkPRErrors, + SilenceForkPRErrorsFlag: config.SilenceForkPRErrorsFlag, DisableApplyAll: userConfig.DisableApplyAll, ProjectCommandBuilder: &events.DefaultProjectCommandBuilder{ ParserValidator: validator, diff --git a/server/user_config.go b/server/user_config.go index b3c101f27c..6ab3495a41 100644 --- a/server/user_config.go +++ b/server/user_config.go @@ -40,6 +40,7 @@ type UserConfig struct { // RequireMergeable is whether to require pull requests to be mergeable before // allowing terraform apply's to run. RequireMergeable bool `mapstructure:"require-mergeable"` + SilenceForkPRErrors bool `mapstructure:"silence-fork-pr-errors"` SilenceWhitelistErrors bool `mapstructure:"silence-whitelist-errors"` SlackToken string `mapstructure:"slack-token"` SSLCertFile string `mapstructure:"ssl-cert-file"`