diff --git a/server/controllers/events/events_controller_e2e_test.go b/server/controllers/events/events_controller_e2e_test.go
index d1e8280f9a..f42175c92a 100644
--- a/server/controllers/events/events_controller_e2e_test.go
+++ b/server/controllers/events/events_controller_e2e_test.go
@@ -93,6 +93,10 @@ func TestGitHubWorkflow(t *testing.T) {
ApplyLock bool
// AllowCommands flag what kind of atlantis commands are available.
AllowCommands []command.Name
+ // DisableAutoplan flag disable auto plans when any pull request is opened.
+ DisableAutoplan bool
+ // DisablePreWorkflowHooks if set to true, pre-workflow hooks will be disabled
+ DisablePreWorkflowHooks bool
// ExpAutomerge is true if we expect Atlantis to automerge.
ExpAutomerge bool
// ExpAutoplan is true if we expect Atlantis to autoplan.
@@ -247,6 +251,25 @@ func TestGitHubWorkflow(t *testing.T) {
{"exp-output-merge.txt"},
},
},
+ {
+ Description: "simple with atlantis.yaml - autoplan disabled",
+ RepoDir: "simple-yaml",
+ ModifiedFiles: []string{"main.tf"},
+ DisableAutoplan: true,
+ DisablePreWorkflowHooks: true,
+ ExpAutoplan: false,
+ Comments: []string{
+ "atlantis plan -w staging",
+ "atlantis plan -w default",
+ "atlantis apply -w staging",
+ },
+ ExpReplies: [][]string{
+ {"exp-output-plan-staging.txt"},
+ {"exp-output-plan-default.txt"},
+ {"exp-output-apply-staging.txt"},
+ {"exp-output-merge.txt"},
+ },
+ },
{
Description: "simple with atlantis.yaml and apply all",
RepoDir: "simple-yaml",
@@ -293,6 +316,23 @@ func TestGitHubWorkflow(t *testing.T) {
{"exp-output-merge-only-staging.txt"},
},
},
+ {
+ Description: "modules staging only - autoplan disabled",
+ RepoDir: "modules",
+ ModifiedFiles: []string{"staging/main.tf"},
+ DisableAutoplan: true,
+ DisablePreWorkflowHooks: true,
+ ExpAutoplan: false,
+ Comments: []string{
+ "atlantis plan -d staging",
+ "atlantis apply -d staging",
+ },
+ ExpReplies: [][]string{
+ {"exp-output-plan-staging.txt"},
+ {"exp-output-apply-staging.txt"},
+ {"exp-output-merge-only-staging.txt"},
+ },
+ },
{
Description: "modules modules only",
RepoDir: "modules",
@@ -590,7 +630,12 @@ func TestGitHubWorkflow(t *testing.T) {
userConfig = server.UserConfig{}
userConfig.DisableApply = c.DisableApply
- opt := setupOption{repoConfigFile: c.RepoConfigFile, allowCommands: c.AllowCommands}
+ opt := setupOption{
+ repoConfigFile: c.RepoConfigFile,
+ allowCommands: c.AllowCommands,
+ disableAutoplan: c.DisableAutoplan,
+ disablePreWorkflowHooks: c.DisablePreWorkflowHooks,
+ }
ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir, opt)
// Set the repo to be cloned through the testing backdoor.
repoDir, headSHA := initializeRepo(t, c.RepoDir)
@@ -630,10 +675,16 @@ func TestGitHubWorkflow(t *testing.T) {
ctrl.Post(w, pullClosedReq)
ResponseContains(t, w, 200, "Pull request cleaned successfully")
- expNumHooks := len(c.Comments) + 1 - c.ExpParseFailedCount
+ expNumHooks := len(c.Comments) - c.ExpParseFailedCount
+ // if auto plan is disabled, hooks will not be called on pull request opened event
+ if !c.DisableAutoplan {
+ expNumHooks++
+ }
// Let's verify the pre-workflow hook was called for each comment including the pull request opened event
- mockPreWorkflowHookRunner.VerifyWasCalled(Times(expNumHooks)).Run(Any[models.WorkflowHookCommandContext](),
- Eq("some dummy command"), Any[string](), Any[string](), Any[string]())
+ if !c.DisablePreWorkflowHooks {
+ mockPreWorkflowHookRunner.VerifyWasCalled(Times(expNumHooks)).Run(Any[models.WorkflowHookCommandContext](),
+ Eq("some dummy command"), Any[string](), Any[string](), Any[string]())
+ }
// Let's verify the post-workflow hook was called for each comment including the pull request opened event
mockPostWorkflowHookRunner.VerifyWasCalled(Times(expNumHooks)).Run(Any[models.WorkflowHookCommandContext](),
Eq("some post dummy command"), Any[string](), Any[string](), Any[string]())
@@ -1212,8 +1263,10 @@ func TestGitHubWorkflowWithPolicyCheck(t *testing.T) {
}
type setupOption struct {
- repoConfigFile string
- allowCommands []command.Name
+ repoConfigFile string
+ allowCommands []command.Name
+ disableAutoplan bool
+ disablePreWorkflowHooks bool
}
func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers.VCSEventsController, *vcsmocks.MockClient, *mocks.MockGithubPullGetter, *events.FileWorkspace) {
@@ -1266,22 +1319,26 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
TestingOverrideHeadCloneURL: "override-me",
Logger: logger,
}
+ var preWorkflowHooks []*valid.WorkflowHook
+ if !opt.disablePreWorkflowHooks {
+ preWorkflowHooks = []*valid.WorkflowHook{
+ {
+ StepName: "global_hook",
+ RunCommand: "some dummy command",
+ },
+ }
+ }
defaultTFVersion := terraformClient.DefaultVersion()
locker := events.NewDefaultWorkingDirLocker()
parser := &config.ParserValidator{}
globalCfgArgs := valid.GlobalCfgArgs{
- RepoConfigFile: opt.repoConfigFile,
- AllowRepoCfg: true,
- MergeableReq: false,
- ApprovedReq: false,
- PreWorkflowHooks: []*valid.WorkflowHook{
- {
- StepName: "global_hook",
- RunCommand: "some dummy command",
- },
- },
+ RepoConfigFile: opt.repoConfigFile,
+ AllowRepoCfg: true,
+ MergeableReq: false,
+ ApprovedReq: false,
+ PreWorkflowHooks: preWorkflowHooks,
PostWorkflowHooks: []*valid.WorkflowHook{
{
StepName: "global_hook",
@@ -1538,6 +1595,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
PreWorkflowHooksCommandRunner: preWorkflowHooksCommandRunner,
PostWorkflowHooksCommandRunner: postWorkflowHooksCommandRunner,
PullStatusFetcher: backend,
+ DisableAutoplan: opt.disableAutoplan,
}
repoAllowlistChecker, err := events.NewRepoAllowlistChecker("*")
diff --git a/server/controllers/events/testdata/test-repos/simple-yaml/exp-output-plan-default.txt b/server/controllers/events/testdata/test-repos/simple-yaml/exp-output-plan-default.txt
new file mode 100644
index 0000000000..c3fb6a9b51
--- /dev/null
+++ b/server/controllers/events/testdata/test-repos/simple-yaml/exp-output-plan-default.txt
@@ -0,0 +1,41 @@
+Ran Plan for dir: `.` workspace: `default`
+
+Show Output
+
+```diff
+preinit
+
+
+Terraform used the selected providers to generate the following execution
+plan. Resource actions are indicated with the following symbols:
++ create
+
+Terraform will perform the following actions:
+
+ # null_resource.simple[0] will be created
++ resource "null_resource" "simple" {
+ + id = (known after apply)
+ }
+
+Plan: 1 to add, 0 to change, 0 to destroy.
+
+Changes to Outputs:
++ var = "fromconfig"
++ workspace = "default"
+
+postplan
+```
+
+* :arrow_forward: To **apply** this plan, comment:
+ * `atlantis apply -d .`
+* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
+* :repeat: To **plan** this project again, comment:
+ * `atlantis plan -d .`
+
+Plan: 1 to add, 0 to change, 0 to destroy.
+
+---
+* :fast_forward: To **apply** all unapplied plans from this pull request, comment:
+ * `atlantis apply`
+* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:
+ * `atlantis unlock`
diff --git a/server/controllers/events/testdata/test-repos/simple-yaml/exp-output-plan-staging.txt b/server/controllers/events/testdata/test-repos/simple-yaml/exp-output-plan-staging.txt
new file mode 100644
index 0000000000..5945110025
--- /dev/null
+++ b/server/controllers/events/testdata/test-repos/simple-yaml/exp-output-plan-staging.txt
@@ -0,0 +1,36 @@
+Ran Plan for dir: `.` workspace: `staging`
+
+Show Output
+
+```diff
+Terraform used the selected providers to generate the following execution
+plan. Resource actions are indicated with the following symbols:
++ create
+
+Terraform will perform the following actions:
+
+ # null_resource.simple[0] will be created
++ resource "null_resource" "simple" {
+ + id = (known after apply)
+ }
+
+Plan: 1 to add, 0 to change, 0 to destroy.
+
+Changes to Outputs:
++ var = "fromfile"
++ workspace = "staging"
+```
+
+* :arrow_forward: To **apply** this plan, comment:
+ * `atlantis apply -w staging`
+* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
+* :repeat: To **plan** this project again, comment:
+ * `atlantis plan -w staging`
+
+Plan: 1 to add, 0 to change, 0 to destroy.
+
+---
+* :fast_forward: To **apply** all unapplied plans from this pull request, comment:
+ * `atlantis apply`
+* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:
+ * `atlantis unlock`
diff --git a/server/events/project_command_builder.go b/server/events/project_command_builder.go
index 2347c9072a..8727058676 100644
--- a/server/events/project_command_builder.go
+++ b/server/events/project_command_builder.go
@@ -509,6 +509,19 @@ func (p *DefaultProjectCommandBuilder) buildProjectPlanCommand(ctx *command.Cont
var pcc []command.ProjectContext
+ ctx.Log.Debug("building plan command")
+ unlockFn, err := p.WorkingDirLocker.TryLock(ctx.Pull.BaseRepo.FullName, ctx.Pull.Num, workspace, DefaultRepoRelDir)
+ if err != nil {
+ return pcc, err
+ }
+ defer unlockFn()
+
+ ctx.Log.Debug("cloning repository")
+ _, _, err = p.WorkingDir.Clone(ctx.HeadRepo, ctx.Pull, DefaultWorkspace)
+ if err != nil {
+ return pcc, err
+ }
+
// use the default repository workspace because it is the only one guaranteed to have an atlantis.yaml,
// other workspaces will not have the file if they are using pre_workflow_hooks to generate it dynamically
defaultRepoDir, err := p.WorkingDir.GetWorkingDir(ctx.Pull.BaseRepo, ctx.Pull, DefaultWorkspace)
@@ -580,17 +593,12 @@ func (p *DefaultProjectCommandBuilder) buildProjectPlanCommand(ctx *command.Cont
}
}
- ctx.Log.Debug("building plan command")
- unlockFn, err := p.WorkingDirLocker.TryLock(ctx.Pull.BaseRepo.FullName, ctx.Pull.Num, workspace, DefaultRepoRelDir)
- if err != nil {
- return pcc, err
- }
- defer unlockFn()
-
- ctx.Log.Debug("cloning repository")
- _, _, err = p.WorkingDir.Clone(ctx.HeadRepo, ctx.Pull, workspace)
- if err != nil {
- return pcc, err
+ if DefaultWorkspace != workspace {
+ ctx.Log.Debug("cloning repository with workspace %s", workspace)
+ _, _, err = p.WorkingDir.Clone(ctx.HeadRepo, ctx.Pull, workspace)
+ if err != nil {
+ return pcc, err
+ }
}
repoRelDir := DefaultRepoRelDir