mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-08-04 15:28:30 +00:00
feat: add ability to track git untracked files (#3724)
This commit is contained in:
@@ -97,6 +97,7 @@ const (
|
||||
GitlabTokenFlag = "gitlab-token"
|
||||
GitlabUserFlag = "gitlab-user"
|
||||
GitlabWebhookSecretFlag = "gitlab-webhook-secret" // nolint: gosec
|
||||
IncludeGitUntrackedFiles = "include-git-untracked-files"
|
||||
APISecretFlag = "api-secret"
|
||||
HidePrevPlanComments = "hide-prev-plan-comments"
|
||||
QuietPolicyChecks = "quiet-policy-checks"
|
||||
@@ -475,6 +476,10 @@ var boolFlags = map[string]boolFlag{
|
||||
"VCS support is limited to: GitHub.",
|
||||
defaultValue: false,
|
||||
},
|
||||
IncludeGitUntrackedFiles: {
|
||||
description: "Include git untracked files in the Atlantis modified file scope.",
|
||||
defaultValue: false,
|
||||
},
|
||||
ParallelPlanFlag: {
|
||||
description: "Run plan operations in parallel.",
|
||||
defaultValue: false,
|
||||
|
||||
@@ -161,16 +161,17 @@ workflows:
|
||||
- run: terraform apply $PLANFILE
|
||||
```
|
||||
|
||||
### cdktf
|
||||
Here are the requirements to enable [cdktf](https://developer.hashicorp.com/terraform/cdktf)
|
||||
### CDKTF
|
||||
Here are the requirements to enable [CDKTF](https://developer.hashicorp.com/terraform/cdktf)
|
||||
|
||||
- A custom image with `cdktf` installed
|
||||
- The autoplan file updated to trigger off of `**/cdk.tf.json`
|
||||
- The output of `cdktf synth` has to be committed to the pull request
|
||||
- Optional: Use `pre_workflow_hooks` to run `cdktf synth` as a double check
|
||||
- A custom image with `CDKTF` installed
|
||||
- Add `**/cdk.tf.json` to the list of Atlantis autoplan files.
|
||||
- Set the `atlantis-include-git-untracked-files` flag so that the Terraform files dynamically generated
|
||||
by CDKTF will be add to the Atlantis modified file list.
|
||||
- Use `pre_workflow_hooks` to run `cdktf synth`
|
||||
- Optional: There isn't a requirement to use a repo `atlantis.yaml` but one can be leveraged if needed.
|
||||
|
||||
#### custom image
|
||||
#### Custom Image
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
@@ -179,11 +180,12 @@ FROM ghcr.io/runatlantis/atlantis:v0.19.7
|
||||
RUN apk add npm && npm i -g cdktf-cli
|
||||
```
|
||||
|
||||
#### server config
|
||||
#### Server Config
|
||||
|
||||
```bash
|
||||
# env variables
|
||||
ATLANTIS_AUTOPLAN_FILE_LIST="**/*.tf,**/*.tfvars,**/*.tfvars.json,**/cdk.tf.json"
|
||||
ATLANTIS_INCLUDE_GIT_UNTRACKED_FILES=true
|
||||
```
|
||||
|
||||
OR
|
||||
@@ -192,9 +194,10 @@ OR
|
||||
```yaml
|
||||
# config.yaml
|
||||
autoplan-file-list: "**/*.tf,**/*.tfvars,**/*.tfvars.json,**/cdk.tf.json"
|
||||
include-git-untracked-files: true
|
||||
```
|
||||
|
||||
#### server repo config
|
||||
#### Server Repo Config
|
||||
|
||||
Use `pre_workflow_hooks`
|
||||
|
||||
@@ -204,32 +207,39 @@ Use `pre_workflow_hooks`
|
||||
repos:
|
||||
- id: /.*cdktf.*/
|
||||
pre_workflow_hooks:
|
||||
- run: npm i && cdktf get && cdktf synth
|
||||
- run: npm i && cdktf get && cdktf synth --output ci-cdktf.out
|
||||
```
|
||||
|
||||
#### repo structure
|
||||
**Note:** don't use the default `cdktf.out` directory that CDKTF uses, as this should be in the `.gitignore` list of the
|
||||
repo, so that locally generated files are not checked in.
|
||||
|
||||
This is the git repo structure after running `cdktf synth`. The `cdk.tf.json` files contain the HCL that atlantis can run.
|
||||
#### Repo Structure
|
||||
|
||||
This is the git repo structure after running `cdktf synth`. The `cdk.tf.json` files contain the Terraform configuration
|
||||
that atlantis can run.
|
||||
|
||||
```bash
|
||||
$ tree --gitignore
|
||||
.
|
||||
├── cdktf.json
|
||||
├── cdktf.out
|
||||
├── ci-cdktf.out
|
||||
│ ├── manifest.json
|
||||
│ └── stacks
|
||||
│ └── eks
|
||||
│ └── cdk.tf.json
|
||||
```
|
||||
|
||||
#### workflow
|
||||
#### Workflow
|
||||
|
||||
1. Container orchestrator (k8s/fargate/ecs/etc) uses the custom docker image of atlantis with `cdktf` installed with the `--autoplan-file-list` to trigger on json files
|
||||
1. PR branch is pushed up containing `cdktf` changes and generated hcl json
|
||||
1. Atlantis checks out the branch in the repo
|
||||
1. Atlantis runs the `npm i && cdktf get && cdktf synth` command in the repo root as a step in `pre_workflow_hooks` (as a double check described above)
|
||||
1. Atlantis detects the change to the generated hcl json files in a number of `dir`s
|
||||
1. Atlantis then runs `terraform` workflows in the respective `dir`s as usual
|
||||
1. Container orchestrator (k8s/fargate/ecs/etc) uses the custom docker image of atlantis with `cdktf` installed with
|
||||
the `--autoplan-file-list` to trigger on `cdk.tf.json` files and `--include-git-untracked-files` set to include the
|
||||
CDKTF dynamically generated Terraform files in the Atlantis plan.
|
||||
1. PR branch is pushed up containing `cdktf` code changes.
|
||||
1. Atlantis checks out the branch in the repo.
|
||||
1. Atlantis runs the `npm i && cdktf get && cdktf synth` command in the repo root as a step in `pre_workflow_hooks`,
|
||||
generating the `cdk.tf.json` Terraform files.
|
||||
1. Atlantis detects the `cdk.tf.json` untracked files in a number of directories.
|
||||
1. Atlantis then runs `terraform` workflows in the respective directories as usual.
|
||||
|
||||
### Terragrunt
|
||||
Atlantis supports running custom commands in place of the default Atlantis
|
||||
|
||||
@@ -626,6 +626,16 @@ This is useful when you have many projects and want to keep the pull request cle
|
||||
Hide previous plan comments to declutter PRs. This is only supported in
|
||||
GitHub and GitLab currently. This is not enabled by default.
|
||||
|
||||
### `--include-git-untracked-files`
|
||||
```bash
|
||||
atlantis server --include-git-untracked-files
|
||||
# or
|
||||
ATLANTIS_INCLUDE_GIT_UNTRACKED_FILES=true
|
||||
```
|
||||
Include git untracked files in the Atlantis modified file list.
|
||||
Used for example with CDKTF pre-workflow hooks that dynamically generate
|
||||
Terraform files.
|
||||
|
||||
### `--locking-db-type`
|
||||
```bash
|
||||
atlantis server --locking-db-type="<boltdb|redis>"
|
||||
|
||||
@@ -1323,6 +1323,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
|
||||
@@ -93,6 +93,25 @@ func (mock *MockWorkingDir) DeletePlan(r models.Repo, p models.PullRequest, work
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockWorkingDir) GetGitUntrackedFiles(r models.Repo, p models.PullRequest, workspace string) ([]string, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockWorkingDir().")
|
||||
}
|
||||
params := []pegomock.Param{r, p, workspace}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("GetGitUntrackedFiles", params, []reflect.Type{reflect.TypeOf((*[]string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 []string
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].([]string)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockWorkingDir) GetPullDir(r models.Repo, p models.PullRequest) (string, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockWorkingDir().")
|
||||
@@ -335,6 +354,41 @@ func (c *MockWorkingDir_DeletePlan_OngoingVerification) GetAllCapturedArguments(
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockWorkingDir) GetGitUntrackedFiles(r models.Repo, p models.PullRequest, workspace string) *MockWorkingDir_GetGitUntrackedFiles_OngoingVerification {
|
||||
params := []pegomock.Param{r, p, workspace}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetGitUntrackedFiles", params, verifier.timeout)
|
||||
return &MockWorkingDir_GetGitUntrackedFiles_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type MockWorkingDir_GetGitUntrackedFiles_OngoingVerification struct {
|
||||
mock *MockWorkingDir
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *MockWorkingDir_GetGitUntrackedFiles_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, string) {
|
||||
r, p, workspace := c.GetAllCapturedArguments()
|
||||
return r[len(r)-1], p[len(p)-1], workspace[len(workspace)-1]
|
||||
}
|
||||
|
||||
func (c *MockWorkingDir_GetGitUntrackedFiles_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []string) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]models.Repo, len(c.methodInvocations))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(models.Repo)
|
||||
}
|
||||
_param1 = make([]models.PullRequest, len(c.methodInvocations))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(models.PullRequest)
|
||||
}
|
||||
_param2 = make([]string, len(c.methodInvocations))
|
||||
for u, param := range params[2] {
|
||||
_param2[u] = param.(string)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockWorkingDir) GetPullDir(r models.Repo, p models.PullRequest) *MockWorkingDir_GetPullDir_OngoingVerification {
|
||||
params := []pegomock.Param{r, p}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetPullDir", params, verifier.timeout)
|
||||
|
||||
@@ -93,6 +93,25 @@ func (mock *MockWorkingDir) DeletePlan(r models.Repo, p models.PullRequest, work
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockWorkingDir) GetGitUntrackedFiles(r models.Repo, p models.PullRequest, workspace string) ([]string, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockWorkingDir().")
|
||||
}
|
||||
params := []pegomock.Param{r, p, workspace}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("GetGitUntrackedFiles", params, []reflect.Type{reflect.TypeOf((*[]string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 []string
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].([]string)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockWorkingDir) GetPullDir(r models.Repo, p models.PullRequest) (string, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockWorkingDir().")
|
||||
@@ -335,6 +354,41 @@ func (c *MockWorkingDir_DeletePlan_OngoingVerification) GetAllCapturedArguments(
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockWorkingDir) GetGitUntrackedFiles(r models.Repo, p models.PullRequest, workspace string) *MockWorkingDir_GetGitUntrackedFiles_OngoingVerification {
|
||||
params := []pegomock.Param{r, p, workspace}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetGitUntrackedFiles", params, verifier.timeout)
|
||||
return &MockWorkingDir_GetGitUntrackedFiles_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type MockWorkingDir_GetGitUntrackedFiles_OngoingVerification struct {
|
||||
mock *MockWorkingDir
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *MockWorkingDir_GetGitUntrackedFiles_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, string) {
|
||||
r, p, workspace := c.GetAllCapturedArguments()
|
||||
return r[len(r)-1], p[len(p)-1], workspace[len(workspace)-1]
|
||||
}
|
||||
|
||||
func (c *MockWorkingDir_GetGitUntrackedFiles_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []string) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]models.Repo, len(c.methodInvocations))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(models.Repo)
|
||||
}
|
||||
_param1 = make([]models.PullRequest, len(c.methodInvocations))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(models.PullRequest)
|
||||
}
|
||||
_param2 = make([]string, len(c.methodInvocations))
|
||||
for u, param := range params[2] {
|
||||
_param2[u] = param.(string)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockWorkingDir) GetPullDir(r models.Repo, p models.PullRequest) *MockWorkingDir_GetPullDir_OngoingVerification {
|
||||
params := []pegomock.Param{r, p}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetPullDir", params, verifier.timeout)
|
||||
|
||||
@@ -53,6 +53,7 @@ func NewInstrumentedProjectCommandBuilder(
|
||||
AutoplanFileList string,
|
||||
RestrictFileList bool,
|
||||
SilenceNoProjects bool,
|
||||
IncludeGitUntrackedFiles bool,
|
||||
scope tally.Scope,
|
||||
logger logging.SimpleLogging,
|
||||
terraformClient terraform.Client,
|
||||
@@ -83,6 +84,7 @@ func NewInstrumentedProjectCommandBuilder(
|
||||
AutoplanFileList,
|
||||
RestrictFileList,
|
||||
SilenceNoProjects,
|
||||
IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -111,27 +113,29 @@ func NewProjectCommandBuilder(
|
||||
AutoplanFileList string,
|
||||
RestrictFileList bool,
|
||||
SilenceNoProjects bool,
|
||||
IncludeGitUntrackedFiles bool,
|
||||
scope tally.Scope,
|
||||
logger logging.SimpleLogging,
|
||||
terraformClient terraform.Client,
|
||||
) *DefaultProjectCommandBuilder {
|
||||
return &DefaultProjectCommandBuilder{
|
||||
ParserValidator: parserValidator,
|
||||
ProjectFinder: projectFinder,
|
||||
VCSClient: vcsClient,
|
||||
WorkingDir: workingDir,
|
||||
WorkingDirLocker: workingDirLocker,
|
||||
GlobalCfg: globalCfg,
|
||||
PendingPlanFinder: pendingPlanFinder,
|
||||
SkipCloneNoChanges: skipCloneNoChanges,
|
||||
EnableRegExpCmd: EnableRegExpCmd,
|
||||
EnableAutoMerge: EnableAutoMerge,
|
||||
EnableParallelPlan: EnableParallelPlan,
|
||||
EnableParallelApply: EnableParallelApply,
|
||||
AutoDetectModuleFiles: AutoDetectModuleFiles,
|
||||
AutoplanFileList: AutoplanFileList,
|
||||
RestrictFileList: RestrictFileList,
|
||||
SilenceNoProjects: SilenceNoProjects,
|
||||
ParserValidator: parserValidator,
|
||||
ProjectFinder: projectFinder,
|
||||
VCSClient: vcsClient,
|
||||
WorkingDir: workingDir,
|
||||
WorkingDirLocker: workingDirLocker,
|
||||
GlobalCfg: globalCfg,
|
||||
PendingPlanFinder: pendingPlanFinder,
|
||||
SkipCloneNoChanges: skipCloneNoChanges,
|
||||
EnableRegExpCmd: EnableRegExpCmd,
|
||||
EnableAutoMerge: EnableAutoMerge,
|
||||
EnableParallelPlan: EnableParallelPlan,
|
||||
EnableParallelApply: EnableParallelApply,
|
||||
AutoDetectModuleFiles: AutoDetectModuleFiles,
|
||||
AutoplanFileList: AutoplanFileList,
|
||||
RestrictFileList: RestrictFileList,
|
||||
SilenceNoProjects: SilenceNoProjects,
|
||||
IncludeGitUntrackedFiles: IncludeGitUntrackedFiles,
|
||||
ProjectCommandContextBuilder: NewProjectCommandContextBuilder(
|
||||
policyChecksSupported,
|
||||
commentBuilder,
|
||||
@@ -184,7 +188,7 @@ type ProjectStateCommandBuilder interface {
|
||||
BuildStateRmCommands(ctx *command.Context, comment *CommentCommand) ([]command.ProjectContext, error)
|
||||
}
|
||||
|
||||
//go:generate pegomock generate --package mocks -o mocks/mock_project_command_builder.go ProjectCommandBuilder
|
||||
//go:generate pegomock generate github.com/runatlantis/atlantis/server/events --package mocks -o mocks/mock_project_command_builder.go ProjectCommandBuilder
|
||||
|
||||
// ProjectCommandBuilder builds commands that run on individual projects.
|
||||
type ProjectCommandBuilder interface {
|
||||
@@ -200,25 +204,46 @@ type ProjectCommandBuilder interface {
|
||||
// This class combines the data from the comment and any atlantis.yaml file or
|
||||
// Atlantis server config and then generates a set of contexts.
|
||||
type DefaultProjectCommandBuilder struct {
|
||||
ParserValidator *config.ParserValidator
|
||||
ProjectFinder ProjectFinder
|
||||
VCSClient vcs.Client
|
||||
WorkingDir WorkingDir
|
||||
WorkingDirLocker WorkingDirLocker
|
||||
GlobalCfg valid.GlobalCfg
|
||||
PendingPlanFinder *DefaultPendingPlanFinder
|
||||
// Parses and validates server-side repo config files and repo-level atlantis.yaml files.
|
||||
ParserValidator *config.ParserValidator
|
||||
// Determines which projects were modified in a given pull request.
|
||||
ProjectFinder ProjectFinder
|
||||
// Used to make API calls to a VCS host like GitHub or GitLab.
|
||||
VCSClient vcs.Client
|
||||
// Handles the workspace on disk for running commands.
|
||||
WorkingDir WorkingDir
|
||||
// Used to prevent multiple commands from executing at the same time for a single repo, pull, and workspace.
|
||||
WorkingDirLocker WorkingDirLocker
|
||||
// The final parsed version of the server-side repo config.
|
||||
GlobalCfg valid.GlobalCfg
|
||||
// Finds unapplied plans.
|
||||
PendingPlanFinder *DefaultPendingPlanFinder
|
||||
// Builds project command contexts for Atlantis commands.
|
||||
ProjectCommandContextBuilder ProjectCommandContextBuilder
|
||||
SkipCloneNoChanges bool
|
||||
EnableRegExpCmd bool
|
||||
EnableAutoMerge bool
|
||||
EnableParallelPlan bool
|
||||
EnableParallelApply bool
|
||||
AutoDetectModuleFiles string
|
||||
AutoplanFileList string
|
||||
EnableDiffMarkdownFormat bool
|
||||
RestrictFileList bool
|
||||
SilenceNoProjects bool
|
||||
TerraformExecutor terraform.Client
|
||||
// User config option: Skip cloning the repo during autoplan if there are no changes to Terraform projects.
|
||||
SkipCloneNoChanges bool
|
||||
// User config option: Enable the use of regular expressions to run plan/apply commands against defined project names.
|
||||
EnableRegExpCmd bool
|
||||
// User config option: Automatically merge pull requests after all plans have been successfully applied.
|
||||
EnableAutoMerge bool
|
||||
// User config option: Whether to run plan operations in parallel.
|
||||
EnableParallelPlan bool
|
||||
// User config option: Whether to run apply operations in parallel.
|
||||
EnableParallelApply bool
|
||||
// User config option: Enables auto-planning of projects when a module dependency in the same repository has changed.
|
||||
AutoDetectModuleFiles string
|
||||
// User config option: List of file patterns to to to check if a directory contains modified files.
|
||||
AutoplanFileList string
|
||||
// User config option: Format Terraform plan output into a markdown-diff friendy format for color-coding purposes.
|
||||
EnableDiffMarkdownFormat bool
|
||||
// User config option: Block plan requests from projects outside the files modified in the pull request.
|
||||
RestrictFileList bool
|
||||
// User config option: Ignore PR if none of the modified files are part of a project.
|
||||
SilenceNoProjects bool
|
||||
// User config option: Include git untracked files in the modified file list.
|
||||
IncludeGitUntrackedFiles bool
|
||||
// Handles the actual running of Terraform commands.
|
||||
TerraformExecutor terraform.Client
|
||||
}
|
||||
|
||||
// See ProjectCommandBuilder.BuildAutoplanCommands.
|
||||
@@ -241,8 +266,11 @@ func (p *DefaultProjectCommandBuilder) BuildAutoplanCommands(ctx *command.Contex
|
||||
// See ProjectCommandBuilder.BuildPlanCommands.
|
||||
func (p *DefaultProjectCommandBuilder) BuildPlanCommands(ctx *command.Context, cmd *CommentCommand) ([]command.ProjectContext, error) {
|
||||
if !cmd.IsForSpecificProject() {
|
||||
ctx.Log.Debug("Building plan command for all affected projects")
|
||||
return p.buildAllCommandsByCfg(ctx, cmd.CommandName(), cmd.SubName, cmd.Flags, cmd.Verbose)
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -296,7 +324,17 @@ func (p *DefaultProjectCommandBuilder) buildAllCommandsByCfg(ctx *command.Contex
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.Log.Debug("%d files were modified in this pull request", len(modifiedFiles))
|
||||
|
||||
if p.IncludeGitUntrackedFiles {
|
||||
ctx.Log.Debug(("'include-git-untracked-files' option is set, getting untracked files"))
|
||||
untrackedFiles, err := p.WorkingDir.GetGitUntrackedFiles(ctx.HeadRepo, ctx.Pull, DefaultWorkspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modifiedFiles = append(modifiedFiles, untrackedFiles...)
|
||||
}
|
||||
|
||||
ctx.Log.Debug("%d files were modified in this pull request. Modified files: %v", len(modifiedFiles), modifiedFiles)
|
||||
|
||||
if p.SkipCloneNoChanges && p.VCSClient.SupportsSingleFileDownload(ctx.Pull.BaseRepo) {
|
||||
repoCfgFile := p.GlobalCfg.RepoConfigFile(ctx.Pull.BaseRepo.ID())
|
||||
@@ -479,12 +517,25 @@ func (p *DefaultProjectCommandBuilder) buildProjectPlanCommand(ctx *command.Cont
|
||||
}
|
||||
|
||||
if p.RestrictFileList {
|
||||
ctx.Log.Debug("'restrict-file-list' option is set, checking modified files")
|
||||
modifiedFiles, err := p.VCSClient.GetModifiedFiles(ctx.Pull.BaseRepo, ctx.Pull)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if p.IncludeGitUntrackedFiles {
|
||||
ctx.Log.Debug(("'include-git-untracked-files' option is set, getting untracked files"))
|
||||
untrackedFiles, err := p.WorkingDir.GetGitUntrackedFiles(ctx.HeadRepo, ctx.Pull, workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modifiedFiles = append(modifiedFiles, untrackedFiles...)
|
||||
}
|
||||
|
||||
ctx.Log.Debug("%d files were modified in this pull request. Modified files: %v", len(modifiedFiles), modifiedFiles)
|
||||
|
||||
if cmd.RepoRelDir != "" {
|
||||
ctx.Log.Debug("Command directory specified: %s", cmd.RepoRelDir)
|
||||
foundDir := false
|
||||
|
||||
for _, f := range modifiedFiles {
|
||||
@@ -499,6 +550,7 @@ func (p *DefaultProjectCommandBuilder) buildProjectPlanCommand(ctx *command.Cont
|
||||
}
|
||||
|
||||
if cmd.ProjectName != "" {
|
||||
ctx.Log.Debug("Command project name specified: %s", cmd.ProjectName)
|
||||
var notFoundFiles = []string{}
|
||||
var repoConfig valid.RepoCfg
|
||||
|
||||
|
||||
@@ -672,6 +672,7 @@ projects:
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -886,6 +887,7 @@ projects:
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1134,6 +1136,7 @@ workflows:
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1289,6 +1292,7 @@ projects:
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
|
||||
@@ -22,6 +22,30 @@ import (
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
var defaultUserConfig = struct {
|
||||
SkipCloneNoChanges bool
|
||||
EnableRegExpCmd bool
|
||||
EnableAutoMerge bool
|
||||
EnableParallelPlan bool
|
||||
EnableParallelApply bool
|
||||
AutoDetectModuleFiles string
|
||||
AutoplanFileList string
|
||||
RestrictFileList bool
|
||||
SilenceNoProjects bool
|
||||
IncludeGitUntrackedFiles bool
|
||||
}{
|
||||
SkipCloneNoChanges: false,
|
||||
EnableRegExpCmd: false,
|
||||
EnableAutoMerge: false,
|
||||
EnableParallelPlan: false,
|
||||
EnableParallelApply: false,
|
||||
AutoDetectModuleFiles: "",
|
||||
AutoplanFileList: "**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
RestrictFileList: false,
|
||||
SilenceNoProjects: false,
|
||||
IncludeGitUntrackedFiles: true,
|
||||
}
|
||||
|
||||
func TestDefaultProjectCommandBuilder_BuildAutoplanCommands(t *testing.T) {
|
||||
// expCtxFields define the ctx fields we're going to assert on.
|
||||
// Since we're focused on autoplanning here, we don't validate all the
|
||||
@@ -122,6 +146,7 @@ projects:
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
terraformClient := terraform_mocks.NewMockClient()
|
||||
When(terraformClient.ListAvailableVersions(Any[logging.SimpleLogging]())).ThenReturn([]string{}, nil)
|
||||
@@ -159,15 +184,16 @@ projects:
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -473,6 +499,7 @@ projects:
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
for _, c := range cases {
|
||||
// NOTE: we're testing both plan and apply here.
|
||||
@@ -513,15 +540,16 @@ projects:
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
true,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
c.EnableAutoMergeUserCfg,
|
||||
c.EnableParallelPlanUserCfg,
|
||||
c.EnableParallelApplyUserCfg,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
c.Silenced,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -663,6 +691,8 @@ projects:
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
userConfig.RestrictFileList = true
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Description+"_"+command.Plan.String(), func(t *testing.T) {
|
||||
@@ -699,15 +729,16 @@ projects:
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
true,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -929,6 +960,8 @@ projects:
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
for name, c := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
@@ -964,15 +997,16 @@ projects:
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
c.ParallelPlanEnabledUserCfg,
|
||||
c.ParallelApplyEnabledUserCfg,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1044,6 +1078,7 @@ func TestDefaultProjectCommandBuilder_BuildMultiApply(t *testing.T) {
|
||||
ThenReturn(tmpDir, nil)
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
globalCfgArgs := valid.GlobalCfgArgs{
|
||||
AllowRepoCfg: false,
|
||||
@@ -1066,15 +1101,16 @@ func TestDefaultProjectCommandBuilder_BuildMultiApply(t *testing.T) {
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1145,6 +1181,8 @@ projects:
|
||||
}
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
terraformClient := terraform_mocks.NewMockClient()
|
||||
When(terraformClient.ListAvailableVersions(Any[logging.SimpleLogging]())).ThenReturn([]string{}, nil)
|
||||
|
||||
@@ -1158,15 +1196,16 @@ projects:
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1212,6 +1251,7 @@ func TestDefaultProjectCommandBuilder_EscapeArgs(t *testing.T) {
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(strings.Join(c.ExtraArgs, " "), func(t *testing.T) {
|
||||
@@ -1246,15 +1286,16 @@ func TestDefaultProjectCommandBuilder_EscapeArgs(t *testing.T) {
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1366,6 +1407,7 @@ projects:
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
for name, testCase := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
@@ -1415,15 +1457,16 @@ projects:
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1482,6 +1525,9 @@ parallel_plan: true`,
|
||||
},
|
||||
}
|
||||
|
||||
userConfig := defaultUserConfig
|
||||
userConfig.SkipCloneNoChanges = true
|
||||
|
||||
for _, c := range cases {
|
||||
RegisterMockTestingT(t)
|
||||
vcsClient := vcsmocks.NewMockClient()
|
||||
@@ -1512,15 +1558,16 @@ parallel_plan: true`,
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1552,6 +1599,7 @@ func TestDefaultProjectCommandBuilder_WithPolicyCheckEnabled_BuildAutoplanComman
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
workingDir := mocks.NewMockWorkingDir()
|
||||
When(workingDir.Clone(Any[models.Repo](), Any[models.PullRequest](), Any[string]())).ThenReturn(tmpDir, false, nil)
|
||||
@@ -1580,15 +1628,16 @@ func TestDefaultProjectCommandBuilder_WithPolicyCheckEnabled_BuildAutoplanComman
|
||||
globalCfg,
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1650,6 +1699,7 @@ func TestDefaultProjectCommandBuilder_BuildVersionCommand(t *testing.T) {
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
|
||||
globalCfgArgs := valid.GlobalCfgArgs{
|
||||
AllowRepoCfg: false,
|
||||
@@ -1670,15 +1720,16 @@ func TestDefaultProjectCommandBuilder_BuildVersionCommand(t *testing.T) {
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
false,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1708,3 +1759,238 @@ func TestDefaultProjectCommandBuilder_BuildVersionCommand(t *testing.T) {
|
||||
Equals(t, "project2", ctxs[3].RepoRelDir)
|
||||
Equals(t, "workspace2", ctxs[3].Workspace)
|
||||
}
|
||||
|
||||
// Test
|
||||
func TestDefaultProjectCommandBuilder_BuildPlanCommands_Single_With_RestrictFileList_And_IncludeGitUntrackedFiles(t *testing.T) {
|
||||
testDir1 := "directory-1"
|
||||
testDir2 := "directory-2"
|
||||
|
||||
cases := []struct {
|
||||
Description string
|
||||
AtlantisYAML string
|
||||
DirectoryStructure map[string]interface{}
|
||||
ModifiedFiles []string
|
||||
UntrackedFiles []string
|
||||
Cmd events.CommentCommand
|
||||
ExpRepoRelDir string
|
||||
ExpErr string
|
||||
}{
|
||||
{
|
||||
Description: "planning a git untracked file project in a modified directory",
|
||||
Cmd: events.CommentCommand{
|
||||
Name: command.Plan,
|
||||
RepoRelDir: testDir1 + "/ci-cdktf.out/stacks/test",
|
||||
Workspace: "default",
|
||||
},
|
||||
DirectoryStructure: map[string]interface{}{
|
||||
testDir1: map[string]interface{}{
|
||||
"main.ts": nil,
|
||||
},
|
||||
},
|
||||
ModifiedFiles: []string{testDir1 + "/main.ts"},
|
||||
UntrackedFiles: []string{testDir1 + "/ci-cdktf.out/stacks/test/cdk.tf.json"},
|
||||
ExpRepoRelDir: testDir1 + "/ci-cdktf.out/stacks/test",
|
||||
},
|
||||
{
|
||||
Description: "planning a git untracked file project outside a modified directory",
|
||||
Cmd: events.CommentCommand{
|
||||
Name: command.Plan,
|
||||
RepoRelDir: testDir2 + "/ci-cdktf.out/stacks/test",
|
||||
Workspace: "default",
|
||||
},
|
||||
DirectoryStructure: map[string]interface{}{
|
||||
testDir1: map[string]interface{}{
|
||||
"main.ts": nil,
|
||||
},
|
||||
},
|
||||
ModifiedFiles: []string{testDir1 + "/main.ts"},
|
||||
UntrackedFiles: []string{testDir1 + "/ci-cdktf.out/stacks/test/cdk.tf.json"},
|
||||
ExpErr: "the dir \"" + testDir2 + "/ci-cdktf.out/stacks/test\" is not in the plan list of this pull request",
|
||||
},
|
||||
}
|
||||
|
||||
globalCfgArgs := valid.GlobalCfgArgs{
|
||||
AllowRepoCfg: true,
|
||||
MergeableReq: false,
|
||||
ApprovedReq: false,
|
||||
UnDivergedReq: false,
|
||||
}
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
userConfig.RestrictFileList = true
|
||||
userConfig.IncludeGitUntrackedFiles = true
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Description+"_"+command.Plan.String(), func(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
tmpDir := DirStructure(t, c.DirectoryStructure)
|
||||
|
||||
workingDir := mocks.NewMockWorkingDir()
|
||||
When(workingDir.Clone(Any[models.Repo](), Any[models.PullRequest](), Any[string]())).ThenReturn(tmpDir, false, nil)
|
||||
When(workingDir.GetWorkingDir(Any[models.Repo](), Any[models.PullRequest](), Any[string]())).ThenReturn(tmpDir, nil)
|
||||
When(workingDir.GetGitUntrackedFiles(Any[models.Repo](), Any[models.PullRequest](), Any[string]())).ThenReturn(c.UntrackedFiles, nil)
|
||||
vcsClient := vcsmocks.NewMockClient()
|
||||
When(vcsClient.GetModifiedFiles(Any[models.Repo](), Any[models.PullRequest]())).ThenReturn(c.ModifiedFiles, nil)
|
||||
if c.AtlantisYAML != "" {
|
||||
err := os.WriteFile(filepath.Join(tmpDir, valid.DefaultAtlantisFile), []byte(c.AtlantisYAML), 0600)
|
||||
Ok(t, err)
|
||||
}
|
||||
|
||||
terraformClient := terraform_mocks.NewMockClient()
|
||||
When(terraformClient.ListAvailableVersions(Any[logging.SimpleLogging]())).ThenReturn([]string{}, nil)
|
||||
|
||||
builder := events.NewProjectCommandBuilder(
|
||||
false, // policyChecksSupported
|
||||
&config.ParserValidator{},
|
||||
&events.DefaultProjectFinder{},
|
||||
vcsClient,
|
||||
workingDir,
|
||||
events.NewDefaultWorkingDirLocker(),
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
)
|
||||
|
||||
var actCtxs []command.ProjectContext
|
||||
var err error
|
||||
actCtxs, err = builder.BuildPlanCommands(&command.Context{
|
||||
Log: logger,
|
||||
Scope: scope,
|
||||
}, &c.Cmd)
|
||||
if c.ExpErr != "" {
|
||||
ErrEquals(t, c.ExpErr, err)
|
||||
return
|
||||
}
|
||||
Ok(t, err)
|
||||
Equals(t, 1, len(actCtxs))
|
||||
actCtx := actCtxs[0]
|
||||
Equals(t, c.ExpRepoRelDir, actCtx.RepoRelDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultProjectCommandBuilder_BuildPlanCommands_with_IncludeGitUntrackedFiles(t *testing.T) {
|
||||
testDir1 := "directory-1"
|
||||
|
||||
cases := []struct {
|
||||
Description string
|
||||
AtlantisYAML string
|
||||
DirectoryStructure map[string]interface{}
|
||||
ModifiedFiles []string
|
||||
UntrackedFiles []string
|
||||
Cmd events.CommentCommand
|
||||
ExpRepoRelDir string
|
||||
ExpErr string
|
||||
}{
|
||||
{
|
||||
Description: "planning with a git untracked file",
|
||||
Cmd: events.CommentCommand{
|
||||
Name: command.Plan,
|
||||
},
|
||||
DirectoryStructure: map[string]interface{}{
|
||||
testDir1: map[string]interface{}{
|
||||
"main.ts": nil,
|
||||
"ci-cdktf.out": map[string]interface{}{
|
||||
"stacks": map[string]interface{}{
|
||||
"test": map[string]interface{}{
|
||||
"cdk.tf.json": nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ModifiedFiles: []string{testDir1 + "/main.ts"},
|
||||
UntrackedFiles: []string{testDir1 + "/ci-cdktf.out/stacks/test/cdk.tf.json"},
|
||||
ExpRepoRelDir: testDir1 + "/ci-cdktf.out/stacks/test",
|
||||
},
|
||||
}
|
||||
|
||||
globalCfgArgs := valid.GlobalCfgArgs{
|
||||
AllowRepoCfg: true,
|
||||
MergeableReq: false,
|
||||
ApprovedReq: false,
|
||||
UnDivergedReq: false,
|
||||
}
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
|
||||
userConfig := defaultUserConfig
|
||||
userConfig.IncludeGitUntrackedFiles = true
|
||||
userConfig.AutoplanFileList = "**/cdk.tf.json"
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Description+"_"+command.Plan.String(), func(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
tmpDir := DirStructure(t, c.DirectoryStructure)
|
||||
|
||||
workingDir := mocks.NewMockWorkingDir()
|
||||
When(workingDir.Clone(Any[models.Repo](), Any[models.PullRequest](), Any[string]())).ThenReturn(tmpDir, false, nil)
|
||||
When(workingDir.GetWorkingDir(Any[models.Repo](), Any[models.PullRequest](), Any[string]())).ThenReturn(tmpDir, nil)
|
||||
When(workingDir.GetGitUntrackedFiles(Any[models.Repo](), Any[models.PullRequest](), Any[string]())).ThenReturn(c.UntrackedFiles, nil)
|
||||
vcsClient := vcsmocks.NewMockClient()
|
||||
When(vcsClient.GetModifiedFiles(Any[models.Repo](), Any[models.PullRequest]())).ThenReturn(c.ModifiedFiles, nil)
|
||||
if c.AtlantisYAML != "" {
|
||||
err := os.WriteFile(filepath.Join(tmpDir, valid.DefaultAtlantisFile), []byte(c.AtlantisYAML), 0600)
|
||||
Ok(t, err)
|
||||
}
|
||||
|
||||
terraformClient := terraform_mocks.NewMockClient()
|
||||
When(terraformClient.ListAvailableVersions(Any[logging.SimpleLogging]())).ThenReturn([]string{}, nil)
|
||||
|
||||
builder := events.NewProjectCommandBuilder(
|
||||
false, // policyChecksSupported
|
||||
&config.ParserValidator{},
|
||||
&events.DefaultProjectFinder{},
|
||||
vcsClient,
|
||||
workingDir,
|
||||
events.NewDefaultWorkingDirLocker(),
|
||||
valid.NewGlobalCfgFromArgs(globalCfgArgs),
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{ExecutableName: "atlantis"},
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
userConfig.EnableAutoMerge,
|
||||
userConfig.EnableParallelPlan,
|
||||
userConfig.EnableParallelApply,
|
||||
userConfig.AutoDetectModuleFiles,
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
)
|
||||
|
||||
var actCtxs []command.ProjectContext
|
||||
var err error
|
||||
actCtxs, err = builder.BuildPlanCommands(&command.Context{
|
||||
Log: logger,
|
||||
Scope: scope,
|
||||
}, &c.Cmd)
|
||||
if c.ExpErr != "" {
|
||||
ErrEquals(t, c.ExpErr, err)
|
||||
return
|
||||
}
|
||||
Ok(t, err)
|
||||
Equals(t, 1, len(actCtxs))
|
||||
actCtx := actCtxs[0]
|
||||
Equals(t, c.ExpRepoRelDir, actCtx.RepoRelDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ func (p *DefaultProjectFinder) DetermineProjects(log logging.SimpleLogging, modi
|
||||
if len(modifiedTerraformFiles) == 0 {
|
||||
return projects
|
||||
}
|
||||
log.Info("filtered modified files to %d .tf or terragrunt.hcl files: %v",
|
||||
log.Info("filtered modified files to %d file(s) in the autoplan file list: %v",
|
||||
len(modifiedTerraformFiles), modifiedTerraformFiles)
|
||||
|
||||
var dirs []string
|
||||
|
||||
@@ -32,8 +32,8 @@ const workingDirPrefix = "repos"
|
||||
|
||||
var cloneLocks sync.Map
|
||||
|
||||
//go:generate pegomock generate --package mocks -o mocks/mock_working_dir.go WorkingDir
|
||||
//go:generate pegomock generate --package events WorkingDir
|
||||
//go:generate pegomock generate github.com/runatlantis/atlantis/server/events --package mocks -o mocks/mock_working_dir.go WorkingDir
|
||||
//go:generate pegomock generate github.com/runatlantis/atlantis/server/events --package events WorkingDir
|
||||
|
||||
// WorkingDir handles the workspace on disk for running commands.
|
||||
type WorkingDir interface {
|
||||
@@ -56,6 +56,8 @@ type WorkingDir interface {
|
||||
SetSafeToReClone()
|
||||
// DeletePlan deletes the plan for this repo, pull, workspace path and project name
|
||||
DeletePlan(r models.Repo, p models.PullRequest, workspace string, path string, projectName string) error
|
||||
// GetGitUntrackedFiles returns a list of Git untracked files in the working dir.
|
||||
GetGitUntrackedFiles(r models.Repo, p models.PullRequest, workspace string) ([]string, error)
|
||||
}
|
||||
|
||||
// FileWorkspace implements WorkingDir with the file system.
|
||||
@@ -382,3 +384,24 @@ func (w *FileWorkspace) DeletePlan(r models.Repo, p models.PullRequest, workspac
|
||||
w.Logger.Info("Deleting plan: " + planPath)
|
||||
return os.Remove(planPath)
|
||||
}
|
||||
|
||||
// getGitUntrackedFiles returns a list of Git untracked files in the working dir.
|
||||
func (w *FileWorkspace) GetGitUntrackedFiles(r models.Repo, p models.PullRequest, workspace string) ([]string, error) {
|
||||
workingDir, err := w.GetWorkingDir(r, p, workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.Logger.Debug("Checking for Git untracked files in directory: '%s'", workingDir)
|
||||
cmd := exec.Command("git", "ls-files", "--others", "--exclude-standard")
|
||||
cmd.Dir = workingDir
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
untrackedFiles := strings.Split(string(output), "\n")[:]
|
||||
w.Logger.Debug("Untracked files: '%s'", strings.Join(untrackedFiles, ","))
|
||||
return untrackedFiles, nil
|
||||
}
|
||||
|
||||
@@ -596,6 +596,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
userConfig.AutoplanFileList,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
|
||||
@@ -60,6 +60,7 @@ type UserConfig struct {
|
||||
GitlabToken string `mapstructure:"gitlab-token"`
|
||||
GitlabUser string `mapstructure:"gitlab-user"`
|
||||
GitlabWebhookSecret string `mapstructure:"gitlab-webhook-secret"`
|
||||
IncludeGitUntrackedFiles bool `mapstructure:"include-git-untracked-files"`
|
||||
APISecret string `mapstructure:"api-secret"`
|
||||
HidePrevPlanComments bool `mapstructure:"hide-prev-plan-comments"`
|
||||
LockingDBType string `mapstructure:"locking-db-type"`
|
||||
|
||||
Reference in New Issue
Block a user