mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-29 05:08:22 +00:00
feat: add autodiscover enabled feature (#3895)
* add flag to allow the user disable the autodiscover * add global config and doc * feat: Implement autodiscover.mode * fix: Minor doc fixes * fix: Small fixes to docs/indent/tests * fix: Line length, quoting, function comments * fix: Add a few more tests and remove newlines * fix: Always camel case never snake --------- Co-authored-by: Marcelo Medeiros <m.medeiros@carepay.com> Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com> Co-authored-by: PePe Amengual <jose.amengual@gmail.com>
This commit is contained in:
@@ -54,6 +54,7 @@ const (
|
||||
AllowForkPRsFlag = "allow-fork-prs"
|
||||
AllowRepoConfigFlag = "allow-repo-config"
|
||||
AtlantisURLFlag = "atlantis-url"
|
||||
AutoDiscoverModeFlag = "autodiscover-mode"
|
||||
AutomergeFlag = "automerge"
|
||||
ParallelPlanFlag = "parallel-plan"
|
||||
ParallelApplyFlag = "parallel-apply"
|
||||
@@ -148,6 +149,7 @@ const (
|
||||
DefaultADBasicUser = ""
|
||||
DefaultADBasicPassword = ""
|
||||
DefaultADHostname = "dev.azure.com"
|
||||
DefaultAutoDiscoverMode = "auto"
|
||||
DefaultAutoplanFileList = "**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl"
|
||||
DefaultAllowCommands = "version,plan,apply,unlock,approve_policies"
|
||||
DefaultCheckoutStrategy = CheckoutStrategyBranch
|
||||
@@ -207,6 +209,12 @@ var stringFlags = map[string]stringFlag{
|
||||
AtlantisURLFlag: {
|
||||
description: "URL that Atlantis can be reached at. Defaults to http://$(hostname):$port where $port is from --" + PortFlag + ". Supports a base path ex. https://example.com/basepath.",
|
||||
},
|
||||
AutoDiscoverModeFlag: {
|
||||
description: "Auto discover mode controls whether projects in a repo are discovered by Atlantis. Defaults to 'auto' which " +
|
||||
"means projects will be discovered when no explicit projects are defined in repo config. Also supports 'enabled' (always " +
|
||||
"discover projects) and 'disabled' (never discover projects).",
|
||||
defaultValue: DefaultAutoDiscoverMode,
|
||||
},
|
||||
AutoplanModulesFromProjects: {
|
||||
description: "Comma separated list of file patterns to select projects Atlantis will index for module dependencies." +
|
||||
" Indexed projects will automatically be planned if a module they depend on is modified." +
|
||||
@@ -871,6 +879,9 @@ func (s *ServerCmd) setDefaults(c *server.UserConfig) {
|
||||
if c.WebPassword == "" {
|
||||
c.WebPassword = DefaultWebPassword
|
||||
}
|
||||
if c.AutoDiscoverModeFlag == "" {
|
||||
c.AutoDiscoverModeFlag = DefaultAutoDiscoverMode
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerCmd) validate(userConfig server.UserConfig) error {
|
||||
|
||||
@@ -60,6 +60,7 @@ var testFlags = map[string]interface{}{
|
||||
AllowCommandsFlag: "version,plan,unlock,import,approve_policies", // apply is disabled by DisableApply
|
||||
AllowForkPRsFlag: true,
|
||||
AllowRepoConfigFlag: true,
|
||||
AutoDiscoverModeFlag: "auto",
|
||||
AutomergeFlag: true,
|
||||
AutoplanFileListFlag: "**/*.tf,**/*.yml",
|
||||
BitbucketBaseURLFlag: "https://bitbucket-base-url.com",
|
||||
|
||||
@@ -34,12 +34,18 @@ By default, this is not allowed.
|
||||
:::
|
||||
|
||||
::: warning
|
||||
Once an `atlantis.yaml` file exists in a repo, Atlantis won't try to determine
|
||||
where to run plan automatically. Instead it will just follow the project configuration.
|
||||
This means that you'll need to define each project in your repo.
|
||||
Once an `atlantis.yaml` file exists in a repo and one or more `projects` are configured,
|
||||
Atlantis won't try to determine where to run plan automatically. Instead it will just
|
||||
follow the project configuration. This means that you'll need to define each project
|
||||
in your repo.
|
||||
|
||||
If you have many directories with Terraform configuration, each directory will
|
||||
need to be defined.
|
||||
|
||||
This behavior can be overriden by setting `autodiscover.mode` to
|
||||
`enabled` in which case Atlantis will still try to discover projects which were not
|
||||
explicitly configured. If the directory of any discovered project conflicts with a
|
||||
manually configured project, the manually configured project will take precedence.
|
||||
:::
|
||||
|
||||
## Example Using All Keys
|
||||
@@ -47,6 +53,8 @@ need to be defined.
|
||||
```yaml
|
||||
version: 3
|
||||
automerge: true
|
||||
autodiscover:
|
||||
mode: auto
|
||||
delete_source_branch_on_merge: true
|
||||
parallel_plan: true
|
||||
parallel_apply: true
|
||||
@@ -281,6 +289,34 @@ in each group one by one.
|
||||
If any plan/apply fails and `abort_on_execution_order_fail` is set to true on a repo level, all the
|
||||
following groups will be aborted. For this example, if project2 fails then project1 will not run.
|
||||
|
||||
### Autodiscovery Config
|
||||
```yaml
|
||||
autodiscover:
|
||||
mode: "auto"
|
||||
```
|
||||
The above is the default configuration for `autodiscover.mode`. When `autodiscover.mode` is auto,
|
||||
projects will be discovered only if the repo has no `projects` configured.
|
||||
|
||||
```yaml
|
||||
autodiscover:
|
||||
mode: "disabled"
|
||||
```
|
||||
With the config above, Atlantis will never try to discover projects, even when there are no
|
||||
`projects` configured. This is useful if dynamically generating Atlantis config in pre_workflow hooks.
|
||||
See [Dynamic Repo Config Generation](pre-workflow-hooks.html#dynamic-repo-config-generation).
|
||||
|
||||
```yaml
|
||||
autodiscover:
|
||||
mode: "enabled"
|
||||
```
|
||||
With the config above, Atlantis will unconditionally try to discover projects based on modified_files,
|
||||
even when the directory of the project is missing from the configured `projects` in the repo configuration.
|
||||
If a discovered project has the same directory as a project which was manually configured in `projects`,
|
||||
the manual configuration will take precedence.
|
||||
|
||||
Use this feature when some projects require specific configuration in a repo with many projects yet
|
||||
it's still desirable for Atlantis to plan/apply for projects not enumerated in the config.
|
||||
|
||||
### Custom Backend Config
|
||||
See [Custom Workflow Use Cases: Custom Backend Config](custom-workflows.html#custom-backend-config)
|
||||
|
||||
|
||||
@@ -106,6 +106,22 @@ Values are chosen in this order:
|
||||
* If a load balancer with a non http/https port (not the one defined in the `--port` flag) is used, update the URL to include the port like in the example above.
|
||||
* This URL is used as the `details` link next to each atlantis job to view the job's logs.
|
||||
|
||||
### `--autodiscover-mode`
|
||||
```bash
|
||||
atlantis server --autodiscover-mode="<auto|enabled|disabled>"
|
||||
# or
|
||||
ATLANTIS_AUTODISCOVER_MODE="<auto|enabled|disabled>"
|
||||
```
|
||||
Sets auto discover mode, default is `auto`. When set to `auto`, projects in a repo will be discovered by
|
||||
Atlantis when there are no projects configured in the repo config. If one or more projects are defined
|
||||
in the repo config then auto discovery will be completely disabled.
|
||||
|
||||
When set to `enabled` projects will be discovered unconditionally. If an auto discovered project is already
|
||||
defined in the projects section of the repo config, the project from the repo config will take precedence over
|
||||
the auto discovered project.
|
||||
|
||||
When set to `disabled` projects will never be discovered, even if there are no projects configured in the repo config.
|
||||
|
||||
### `--automerge`
|
||||
```bash
|
||||
atlantis server --automerge
|
||||
|
||||
@@ -88,6 +88,10 @@ repos:
|
||||
# policy_check defines if policy checking should be enable on this repository.
|
||||
policy_check: false
|
||||
|
||||
# autodiscover defines how atlantis should automatically discover projects in this repository.
|
||||
autodiscover:
|
||||
mode: auto
|
||||
|
||||
# id can also be an exact match.
|
||||
- id: github.com/myorg/specific-repo
|
||||
|
||||
@@ -496,6 +500,7 @@ If you set a workflow with the key `default`, it will override this.
|
||||
| repo_locking | bool | false | no | Whether or not to get a lock. |
|
||||
| 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
|
||||
|
||||
|
||||
:::tip Notes
|
||||
|
||||
@@ -1352,6 +1352,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"auto",
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
|
||||
@@ -1329,6 +1329,22 @@ 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.).).",
|
||||
},
|
||||
"disable autodiscover": {
|
||||
input: `repos:
|
||||
- id: /.*/
|
||||
autodiscover:
|
||||
mode: disabled`,
|
||||
exp: valid.GlobalCfg{
|
||||
Repos: []valid.Repo{
|
||||
defaultCfg.Repos[0],
|
||||
{
|
||||
IDRegex: regexp.MustCompile(".*"),
|
||||
AutoDiscover: &valid.AutoDiscover{Mode: valid.AutoDiscoverDisabledMode},
|
||||
},
|
||||
},
|
||||
Workflows: defaultCfg.Workflows,
|
||||
},
|
||||
},
|
||||
"no workflows key": {
|
||||
input: `repos: []`,
|
||||
exp: defaultCfg,
|
||||
@@ -1404,6 +1420,8 @@ repos:
|
||||
allowed_overrides: [plan_requirements, apply_requirements, import_requirements, workflow, delete_source_branch_on_merge]
|
||||
allow_custom_workflows: true
|
||||
policy_check: true
|
||||
autodiscover:
|
||||
mode: enabled
|
||||
- id: /.*/
|
||||
branch: /(master|main)/
|
||||
pre_workflow_hooks:
|
||||
@@ -1411,6 +1429,8 @@ repos:
|
||||
post_workflow_hooks:
|
||||
- run: custom workflow command
|
||||
policy_check: false
|
||||
autodiscover:
|
||||
mode: disabled
|
||||
workflows:
|
||||
custom1:
|
||||
plan:
|
||||
@@ -1457,6 +1477,7 @@ policies:
|
||||
AllowedOverrides: []string{"plan_requirements", "apply_requirements", "import_requirements", "workflow", "delete_source_branch_on_merge"},
|
||||
AllowCustomWorkflows: Bool(true),
|
||||
PolicyCheck: Bool(true),
|
||||
AutoDiscover: &valid.AutoDiscover{Mode: valid.AutoDiscoverEnabledMode},
|
||||
},
|
||||
{
|
||||
IDRegex: regexp.MustCompile(".*"),
|
||||
@@ -1464,6 +1485,7 @@ policies:
|
||||
PreWorkflowHooks: preWorkflowHooks,
|
||||
PostWorkflowHooks: postWorkflowHooks,
|
||||
PolicyCheck: Bool(false),
|
||||
AutoDiscover: &valid.AutoDiscover{Mode: valid.AutoDiscoverDisabledMode},
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
@@ -1574,6 +1596,7 @@ workflows:
|
||||
RepoLocking: Bool(true),
|
||||
PolicyCheck: Bool(false),
|
||||
CustomPolicyCheck: Bool(false),
|
||||
AutoDiscover: raw.DefaultAutoDiscover(),
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
@@ -1727,7 +1750,10 @@ func TestParserValidator_ParseGlobalCfgJSON(t *testing.T) {
|
||||
"allowed_workflows": ["custom"],
|
||||
"apply_requirements": ["mergeable", "approved"],
|
||||
"allowed_overrides": ["workflow", "apply_requirements"],
|
||||
"allow_custom_workflows": true
|
||||
"allow_custom_workflows": true,
|
||||
"autodiscover": {
|
||||
"mode": "enabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "github.com/owner/repo"
|
||||
@@ -1792,6 +1818,7 @@ func TestParserValidator_ParseGlobalCfgJSON(t *testing.T) {
|
||||
AllowedWorkflows: []string{"custom"},
|
||||
AllowedOverrides: []string{"workflow", "apply_requirements"},
|
||||
AllowCustomWorkflows: Bool(true),
|
||||
AutoDiscover: &valid.AutoDiscover{Mode: valid.AutoDiscoverEnabledMode},
|
||||
},
|
||||
{
|
||||
ID: "github.com/owner/repo",
|
||||
@@ -1799,6 +1826,7 @@ func TestParserValidator_ParseGlobalCfgJSON(t *testing.T) {
|
||||
ApplyRequirements: nil,
|
||||
AllowedOverrides: nil,
|
||||
AllowCustomWorkflows: nil,
|
||||
AutoDiscover: nil,
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
|
||||
38
server/core/config/raw/autodiscover.go
Normal file
38
server/core/config/raw/autodiscover.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package raw
|
||||
|
||||
import (
|
||||
validation "github.com/go-ozzo/ozzo-validation"
|
||||
"github.com/runatlantis/atlantis/server/core/config/valid"
|
||||
)
|
||||
|
||||
var DefaultAutoDiscoverMode = valid.AutoDiscoverAutoMode
|
||||
|
||||
type AutoDiscover struct {
|
||||
Mode *valid.AutoDiscoverMode `yaml:"mode,omitempty"`
|
||||
}
|
||||
|
||||
func (a AutoDiscover) ToValid() *valid.AutoDiscover {
|
||||
var v valid.AutoDiscover
|
||||
|
||||
if a.Mode != nil {
|
||||
v.Mode = *a.Mode
|
||||
} else {
|
||||
v.Mode = DefaultAutoDiscoverMode
|
||||
}
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
func (a AutoDiscover) Validate() error {
|
||||
res := validation.ValidateStruct(&a,
|
||||
// If a.Mode is nil, this should still pass validation.
|
||||
validation.Field(&a.Mode, validation.In(valid.AutoDiscoverAutoMode, valid.AutoDiscoverDisabledMode, valid.AutoDiscoverEnabledMode)),
|
||||
)
|
||||
return res
|
||||
}
|
||||
|
||||
func DefaultAutoDiscover() *valid.AutoDiscover {
|
||||
return &valid.AutoDiscover{
|
||||
Mode: DefaultAutoDiscoverMode,
|
||||
}
|
||||
}
|
||||
131
server/core/config/raw/autodiscover_test.go
Normal file
131
server/core/config/raw/autodiscover_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package raw_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/runatlantis/atlantis/server/core/config/raw"
|
||||
"github.com/runatlantis/atlantis/server/core/config/valid"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestAutoDiscover_UnmarshalYAML(t *testing.T) {
|
||||
autoDiscoverEnabled := valid.AutoDiscoverEnabledMode
|
||||
cases := []struct {
|
||||
description string
|
||||
input string
|
||||
exp raw.AutoDiscover
|
||||
}{
|
||||
{
|
||||
description: "omit unset fields",
|
||||
input: "",
|
||||
exp: raw.AutoDiscover{
|
||||
Mode: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "all fields set",
|
||||
input: `
|
||||
mode: enabled
|
||||
`,
|
||||
exp: raw.AutoDiscover{
|
||||
Mode: &autoDiscoverEnabled,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.description, func(t *testing.T) {
|
||||
var a raw.AutoDiscover
|
||||
err := yaml.UnmarshalStrict([]byte(c.input), &a)
|
||||
Ok(t, err)
|
||||
Equals(t, c.exp, a)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoDiscover_Validate(t *testing.T) {
|
||||
autoDiscoverAuto := valid.AutoDiscoverAutoMode
|
||||
autoDiscoverEnabled := valid.AutoDiscoverEnabledMode
|
||||
autoDiscoverDisabled := valid.AutoDiscoverDisabledMode
|
||||
randomString := valid.AutoDiscoverMode("random_string")
|
||||
cases := []struct {
|
||||
description string
|
||||
input raw.AutoDiscover
|
||||
errContains *string
|
||||
}{
|
||||
{
|
||||
description: "nothing set",
|
||||
input: raw.AutoDiscover{},
|
||||
errContains: nil,
|
||||
},
|
||||
{
|
||||
description: "mode set to auto",
|
||||
input: raw.AutoDiscover{
|
||||
Mode: &autoDiscoverAuto,
|
||||
},
|
||||
errContains: nil,
|
||||
},
|
||||
{
|
||||
description: "mode set to disabled",
|
||||
input: raw.AutoDiscover{
|
||||
Mode: &autoDiscoverDisabled,
|
||||
},
|
||||
errContains: nil,
|
||||
},
|
||||
{
|
||||
description: "mode set to enabled",
|
||||
input: raw.AutoDiscover{
|
||||
Mode: &autoDiscoverEnabled,
|
||||
},
|
||||
errContains: nil,
|
||||
},
|
||||
{
|
||||
description: "mode set to random string",
|
||||
input: raw.AutoDiscover{
|
||||
Mode: &randomString,
|
||||
},
|
||||
errContains: String("valid value"),
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.description, func(t *testing.T) {
|
||||
if c.errContains == nil {
|
||||
Ok(t, c.input.Validate())
|
||||
} else {
|
||||
ErrContains(t, *c.errContains, c.input.Validate())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoDiscover_ToValid(t *testing.T) {
|
||||
autoDiscoverEnabled := valid.AutoDiscoverEnabledMode
|
||||
cases := []struct {
|
||||
description string
|
||||
input raw.AutoDiscover
|
||||
exp *valid.AutoDiscover
|
||||
}{
|
||||
{
|
||||
description: "nothing set",
|
||||
input: raw.AutoDiscover{},
|
||||
exp: &valid.AutoDiscover{
|
||||
Mode: valid.AutoDiscoverAutoMode,
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "value set",
|
||||
input: raw.AutoDiscover{
|
||||
Mode: &autoDiscoverEnabled,
|
||||
},
|
||||
exp: &valid.AutoDiscover{
|
||||
Mode: valid.AutoDiscoverEnabledMode,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.description, func(t *testing.T) {
|
||||
Equals(t, c.exp, c.input.ToValid())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ type Repo struct {
|
||||
RepoLocking *bool `yaml:"repo_locking,omitempty" json:"repo_locking,omitempty"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (g GlobalCfg) Validate() error {
|
||||
@@ -211,6 +212,14 @@ func (r Repo) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
autoDiscoverValid := func(value interface{}) error {
|
||||
autoDiscover := value.(*AutoDiscover)
|
||||
if autoDiscover != nil {
|
||||
return autoDiscover.Validate()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return validation.ValidateStruct(&r,
|
||||
validation.Field(&r.ID, validation.Required, validation.By(idValid)),
|
||||
validation.Field(&r.Branch, validation.By(branchValid)),
|
||||
@@ -221,6 +230,7 @@ func (r Repo) Validate() error {
|
||||
validation.Field(&r.ImportRequirements, validation.By(validImportReq)),
|
||||
validation.Field(&r.Workflow, validation.By(workflowExists)),
|
||||
validation.Field(&r.DeleteSourceBranchOnMerge, validation.By(deleteSourceBranchOnMergeValid)),
|
||||
validation.Field(&r.AutoDiscover, validation.By(autoDiscoverValid)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -281,7 +291,7 @@ OuterGlobalPlanReqs:
|
||||
}
|
||||
|
||||
// dont add policy_check step if repo have it explicitly disabled
|
||||
if globalReq == valid.PoliciesPassedCommandReq && r.PolicyCheck != nil && *r.PolicyCheck == false {
|
||||
if globalReq == valid.PoliciesPassedCommandReq && r.PolicyCheck != nil && !*r.PolicyCheck {
|
||||
continue
|
||||
}
|
||||
mergedPlanReqs = append(mergedPlanReqs, globalReq)
|
||||
@@ -295,7 +305,7 @@ OuterGlobalApplyReqs:
|
||||
}
|
||||
|
||||
// dont add policy_check step if repo have it explicitly disabled
|
||||
if globalReq == valid.PoliciesPassedCommandReq && r.PolicyCheck != nil && *r.PolicyCheck == false {
|
||||
if globalReq == valid.PoliciesPassedCommandReq && r.PolicyCheck != nil && !*r.PolicyCheck {
|
||||
continue
|
||||
}
|
||||
mergedApplyReqs = append(mergedApplyReqs, globalReq)
|
||||
@@ -309,12 +319,17 @@ OuterGlobalImportReqs:
|
||||
}
|
||||
|
||||
// dont add policy_check step if repo have it explicitly disabled
|
||||
if globalReq == valid.PoliciesPassedCommandReq && r.PolicyCheck != nil && *r.PolicyCheck == false {
|
||||
if globalReq == valid.PoliciesPassedCommandReq && r.PolicyCheck != nil && !*r.PolicyCheck {
|
||||
continue
|
||||
}
|
||||
mergedImportReqs = append(mergedImportReqs, globalReq)
|
||||
}
|
||||
|
||||
var autoDiscover *valid.AutoDiscover
|
||||
if r.AutoDiscover != nil {
|
||||
autoDiscover = r.AutoDiscover.ToValid()
|
||||
}
|
||||
|
||||
return valid.Repo{
|
||||
ID: id,
|
||||
IDRegex: idRegex,
|
||||
@@ -333,5 +348,6 @@ OuterGlobalImportReqs:
|
||||
RepoLocking: r.RepoLocking,
|
||||
PolicyCheck: r.PolicyCheck,
|
||||
CustomPolicyCheck: r.CustomPolicyCheck,
|
||||
AutoDiscover: autoDiscover,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ type RepoCfg struct {
|
||||
Projects []Project `yaml:"projects,omitempty"`
|
||||
Workflows map[string]Workflow `yaml:"workflows,omitempty"`
|
||||
PolicySets PolicySets `yaml:"policies,omitempty"`
|
||||
AutoDiscover *AutoDiscover `yaml:"autodiscover,omitempty"`
|
||||
Automerge *bool `yaml:"automerge,omitempty"`
|
||||
ParallelApply *bool `yaml:"parallel_apply,omitempty"`
|
||||
ParallelPlan *bool `yaml:"parallel_plan,omitempty"`
|
||||
@@ -71,10 +72,16 @@ func (r RepoCfg) ToValid() valid.RepoCfg {
|
||||
abortOnExcecutionOrderFail = *r.AbortOnExcecutionOrderFail
|
||||
}
|
||||
|
||||
var autoDiscover *valid.AutoDiscover
|
||||
if r.AutoDiscover != nil {
|
||||
autoDiscover = r.AutoDiscover.ToValid()
|
||||
}
|
||||
|
||||
return valid.RepoCfg{
|
||||
Version: *r.Version,
|
||||
Projects: validProjects,
|
||||
Workflows: validWorkflows,
|
||||
AutoDiscover: autoDiscover,
|
||||
Automerge: automerge,
|
||||
ParallelApply: parallelApply,
|
||||
ParallelPlan: parallelPlan,
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestConfig_UnmarshalYAML(t *testing.T) {
|
||||
autoDiscoverEnabled := valid.AutoDiscoverEnabledMode
|
||||
cases := []struct {
|
||||
description string
|
||||
input string
|
||||
@@ -126,6 +127,8 @@ func TestConfig_UnmarshalYAML(t *testing.T) {
|
||||
input: `
|
||||
version: 3
|
||||
automerge: true
|
||||
autodiscover:
|
||||
mode: enabled
|
||||
parallel_apply: true
|
||||
parallel_plan: false
|
||||
projects:
|
||||
@@ -150,6 +153,7 @@ allowed_regexp_prefixes:
|
||||
- staging/`,
|
||||
exp: raw.RepoCfg{
|
||||
Version: Int(3),
|
||||
AutoDiscover: &raw.AutoDiscover{Mode: &autoDiscoverEnabled},
|
||||
Automerge: Bool(true),
|
||||
ParallelApply: Bool(true),
|
||||
ParallelPlan: Bool(false),
|
||||
@@ -232,6 +236,7 @@ func TestConfig_Validate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfig_ToValid(t *testing.T) {
|
||||
autoDiscoverEnabled := valid.AutoDiscoverEnabledMode
|
||||
cases := []struct {
|
||||
description string
|
||||
input raw.RepoCfg
|
||||
@@ -248,18 +253,20 @@ func TestConfig_ToValid(t *testing.T) {
|
||||
{
|
||||
description: "set to empty",
|
||||
input: raw.RepoCfg{
|
||||
Version: Int(2),
|
||||
Workflows: map[string]raw.Workflow{},
|
||||
Projects: []raw.Project{},
|
||||
Version: Int(2),
|
||||
AutoDiscover: &raw.AutoDiscover{},
|
||||
Workflows: map[string]raw.Workflow{},
|
||||
Projects: []raw.Project{},
|
||||
},
|
||||
exp: valid.RepoCfg{
|
||||
Version: 2,
|
||||
Workflows: map[string]valid.Workflow{},
|
||||
Projects: nil,
|
||||
Version: 2,
|
||||
AutoDiscover: raw.DefaultAutoDiscover(),
|
||||
Workflows: map[string]valid.Workflow{},
|
||||
Projects: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "automerge, parallel_apply and abort_on_execution_order_fail omitted",
|
||||
description: "automerge, parallel_apply, abort_on_execution_order_fail omitted",
|
||||
input: raw.RepoCfg{
|
||||
Version: Int(2),
|
||||
},
|
||||
@@ -272,7 +279,7 @@ func TestConfig_ToValid(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "automerge, parallel_apply and abort_on_execution_order_fail true",
|
||||
description: "automerge, parallel_apply, abort_on_execution_order_fail true",
|
||||
input: raw.RepoCfg{
|
||||
Version: Int(2),
|
||||
Automerge: Bool(true),
|
||||
@@ -288,7 +295,7 @@ func TestConfig_ToValid(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "automerge, parallel_apply and abort_on_execution_order_fail false",
|
||||
description: "automerge, parallel_apply, abort_on_execution_order_fail false",
|
||||
input: raw.RepoCfg{
|
||||
Version: Int(2),
|
||||
Automerge: Bool(false),
|
||||
@@ -303,6 +310,30 @@ func TestConfig_ToValid(t *testing.T) {
|
||||
Workflows: map[string]valid.Workflow{},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "autodiscover omitted",
|
||||
input: raw.RepoCfg{
|
||||
Version: Int(2),
|
||||
},
|
||||
exp: valid.RepoCfg{
|
||||
Version: 2,
|
||||
Workflows: map[string]valid.Workflow{},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "autodiscover included",
|
||||
input: raw.RepoCfg{
|
||||
Version: Int(2),
|
||||
AutoDiscover: &raw.AutoDiscover{Mode: &autoDiscoverEnabled},
|
||||
},
|
||||
exp: valid.RepoCfg{
|
||||
Version: 2,
|
||||
AutoDiscover: &valid.AutoDiscover{
|
||||
Mode: valid.AutoDiscoverEnabledMode,
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "only plan stage set",
|
||||
input: raw.RepoCfg{
|
||||
@@ -339,6 +370,9 @@ func TestConfig_ToValid(t *testing.T) {
|
||||
Version: Int(2),
|
||||
Automerge: Bool(true),
|
||||
ParallelApply: Bool(true),
|
||||
AutoDiscover: &raw.AutoDiscover{
|
||||
Mode: &autoDiscoverEnabled,
|
||||
},
|
||||
Workflows: map[string]raw.Workflow{
|
||||
"myworkflow": {
|
||||
Apply: &raw.Stage{
|
||||
@@ -388,6 +422,9 @@ func TestConfig_ToValid(t *testing.T) {
|
||||
Version: 2,
|
||||
Automerge: Bool(true),
|
||||
ParallelApply: Bool(true),
|
||||
AutoDiscover: &valid.AutoDiscover{
|
||||
Mode: valid.AutoDiscoverEnabledMode,
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
"myworkflow": {
|
||||
Name: "myworkflow",
|
||||
|
||||
14
server/core/config/valid/autodiscover.go
Normal file
14
server/core/config/valid/autodiscover.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package valid
|
||||
|
||||
// AutoDiscoverMode enum
|
||||
type AutoDiscoverMode string
|
||||
|
||||
const (
|
||||
AutoDiscoverEnabledMode AutoDiscoverMode = "enabled"
|
||||
AutoDiscoverDisabledMode AutoDiscoverMode = "disabled"
|
||||
AutoDiscoverAutoMode AutoDiscoverMode = "auto"
|
||||
)
|
||||
|
||||
type AutoDiscover struct {
|
||||
Mode AutoDiscoverMode
|
||||
}
|
||||
@@ -28,6 +28,7 @@ const DeleteSourceBranchOnMergeKey = "delete_source_branch_on_merge"
|
||||
const RepoLockingKey = "repo_locking"
|
||||
const PolicyCheckKey = "policy_check"
|
||||
const CustomPolicyCheckKey = "custom_policy_check"
|
||||
const AutoDiscoverKey = "autodiscover"
|
||||
|
||||
// DefaultAtlantisFile is the default name of the config file for each repo.
|
||||
const DefaultAtlantisFile = "atlantis.yaml"
|
||||
@@ -84,6 +85,7 @@ type Repo struct {
|
||||
RepoLocking *bool
|
||||
PolicyCheck *bool
|
||||
CustomPolicyCheck *bool
|
||||
AutoDiscover *AutoDiscover
|
||||
}
|
||||
|
||||
type MergedProjectCfg struct {
|
||||
@@ -245,6 +247,7 @@ func NewGlobalCfgFromArgs(args GlobalCfgArgs) GlobalCfg {
|
||||
deleteSourceBranchOnMerge := false
|
||||
repoLockingKey := true
|
||||
customPolicyCheck := false
|
||||
autoDiscover := AutoDiscover{Mode: AutoDiscoverAutoMode}
|
||||
if args.AllowRepoCfg {
|
||||
allowedOverrides = []string{PlanRequirementsKey, ApplyRequirementsKey, ImportRequirementsKey, WorkflowKey, DeleteSourceBranchOnMergeKey, RepoLockingKey, PolicyCheckKey}
|
||||
allowCustomWorkflows = true
|
||||
@@ -269,6 +272,7 @@ func NewGlobalCfgFromArgs(args GlobalCfgArgs) GlobalCfg {
|
||||
RepoLocking: &repoLockingKey,
|
||||
PolicyCheck: &policyCheck,
|
||||
CustomPolicyCheck: &customPolicyCheck,
|
||||
AutoDiscover: &autoDiscover,
|
||||
},
|
||||
},
|
||||
Workflows: map[string]Workflow{
|
||||
@@ -305,7 +309,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, repoLocking, policyCheck, customPolicyCheck := g.getMatchingCfg(log, repoID)
|
||||
planReqs, applyReqs, importReqs, workflow, allowedOverrides, allowCustomWorkflows, deleteSourceBranchOnMerge, repoLocking, policyCheck, customPolicyCheck, _ := g.getMatchingCfg(log, repoID)
|
||||
|
||||
// If repos are allowed to override certain keys then override them.
|
||||
for _, key := range allowedOverrides {
|
||||
@@ -407,7 +411,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, repoLocking, policyCheck, customPolicyCheck := g.getMatchingCfg(log, repoID)
|
||||
planReqs, applyReqs, importReqs, workflow, _, _, deleteSourceBranchOnMerge, repoLocking, policyCheck, customPolicyCheck, _ := g.getMatchingCfg(log, repoID)
|
||||
return MergedProjectCfg{
|
||||
PlanRequirements: planReqs,
|
||||
ApplyRequirements: applyReqs,
|
||||
@@ -426,6 +430,17 @@ func (g GlobalCfg) DefaultProjCfg(log logging.SimpleLogging, repoID string, repo
|
||||
}
|
||||
}
|
||||
|
||||
// RepoAutoDiscoverCfg returns the AutoDiscover config from the global config
|
||||
// for the repo with id repoID. If no matching repo is found or there is no
|
||||
// AutoDiscover config then this function returns nil.
|
||||
func (g GlobalCfg) RepoAutoDiscoverCfg(repoID string) *AutoDiscover {
|
||||
repo := g.MatchingRepo(repoID)
|
||||
if repo != nil {
|
||||
return repo.AutoDiscover
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateRepoCfg validates that rCfg for repo with id repoID is valid based
|
||||
// on our global config.
|
||||
func (g GlobalCfg) ValidateRepoCfg(rCfg RepoCfg, repoID string) error {
|
||||
@@ -528,7 +543,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, repoLocking bool, policyCheck bool, customPolicyCheck bool) {
|
||||
func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (planReqs []string, applyReqs []string, importReqs []string, workflow Workflow, allowedOverrides []string, allowCustomWorkflows bool, deleteSourceBranchOnMerge bool, repoLocking bool, policyCheck bool, customPolicyCheck bool, autoDiscover AutoDiscover) {
|
||||
toLog := make(map[string]string)
|
||||
traceF := func(repoIdx int, repoID string, key string, val interface{}) string {
|
||||
from := "default server config"
|
||||
@@ -550,6 +565,9 @@ func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (pla
|
||||
return fmt.Sprintf("setting %s: %s from %s", key, valStr, from)
|
||||
}
|
||||
|
||||
// Can't use raw.DefaultAutoDiscoverMode() because of an import cycle. Should refactor to avoid that.
|
||||
autoDiscover = AutoDiscover{Mode: AutoDiscoverAutoMode}
|
||||
|
||||
for _, key := range []string{PlanRequirementsKey, ApplyRequirementsKey, ImportRequirementsKey, WorkflowKey, AllowedOverridesKey, AllowCustomWorkflowsKey, DeleteSourceBranchOnMergeKey, RepoLockingKey, PolicyCheckKey, CustomPolicyCheckKey} {
|
||||
for i, repo := range g.Repos {
|
||||
if repo.IDMatches(repoID) {
|
||||
@@ -604,6 +622,11 @@ func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (pla
|
||||
toLog[CustomPolicyCheckKey] = traceF(i, repo.IDString(), CustomPolicyCheckKey, *repo.CustomPolicyCheck)
|
||||
customPolicyCheck = *repo.CustomPolicyCheck
|
||||
}
|
||||
case AutoDiscoverKey:
|
||||
if repo.AutoDiscover != nil {
|
||||
toLog[AutoDiscoverKey] = traceF(i, repo.IDString(), AutoDiscoverKey, repo.AutoDiscover.Mode)
|
||||
autoDiscover = *repo.AutoDiscover
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/mohae/deepcopy"
|
||||
"github.com/runatlantis/atlantis/server/core/config"
|
||||
"github.com/runatlantis/atlantis/server/core/config/raw"
|
||||
"github.com/runatlantis/atlantis/server/core/config/valid"
|
||||
"github.com/runatlantis/atlantis/server/logging"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
@@ -82,6 +83,7 @@ func TestNewGlobalCfg(t *testing.T) {
|
||||
RepoLocking: Bool(true),
|
||||
PolicyCheck: Bool(false),
|
||||
CustomPolicyCheck: Bool(false),
|
||||
AutoDiscover: raw.DefaultAutoDiscover(),
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
|
||||
@@ -19,6 +19,7 @@ type RepoCfg struct {
|
||||
Workflows map[string]Workflow
|
||||
PolicySets PolicySets
|
||||
Automerge *bool
|
||||
AutoDiscover *AutoDiscover
|
||||
ParallelApply *bool
|
||||
ParallelPlan *bool
|
||||
ParallelPolicyCheck *bool
|
||||
@@ -91,6 +92,24 @@ func isRegexAllowed(name string, allowedRegexpPrefixes []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// This function returns a final true/false decision for whether AutoDiscover is enabled
|
||||
// for a repo. It takes into account the defaultAutoDiscoverMode when there is no explicit
|
||||
// repo config. The defaultAutoDiscoverMode param should be understood as the default
|
||||
// AutoDiscover mode as may be set via CLI params or server side repo config.
|
||||
func (r RepoCfg) AutoDiscoverEnabled(defaultAutoDiscoverMode AutoDiscoverMode) bool {
|
||||
autoDiscoverMode := defaultAutoDiscoverMode
|
||||
if r.AutoDiscover != nil {
|
||||
autoDiscoverMode = r.AutoDiscover.Mode
|
||||
}
|
||||
|
||||
if autoDiscoverMode == AutoDiscoverAutoMode {
|
||||
// AutoDiscover is enabled by default when no projects are defined
|
||||
return len(r.Projects) == 0
|
||||
}
|
||||
|
||||
return autoDiscoverMode == AutoDiscoverEnabledMode
|
||||
}
|
||||
|
||||
// validateWorkspaceAllowed returns an error if repoCfg defines projects in
|
||||
// repoRelDir but none of them use workspace. We want this to be an error
|
||||
// because if users have gone to the trouble of defining projects in repoRelDir
|
||||
|
||||
@@ -216,3 +216,110 @@ func TestConfig_FindProjectsByDir(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_AutoDiscoverEnabled(t *testing.T) {
|
||||
cases := []struct {
|
||||
description string
|
||||
repoAutoDiscover valid.AutoDiscoverMode
|
||||
defaultAutoDiscover valid.AutoDiscoverMode
|
||||
projects []valid.Project
|
||||
expEnabled bool
|
||||
}{
|
||||
{
|
||||
description: "repo disabled autodiscover default enabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo disabled autodiscover default disabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo enabled autodiscover default enabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo enabled autodiscover default disabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo set auto autodiscover with no projects default enabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo set auto autodiscover with no projects default disabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo set auto autodiscover with a project default enabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
projects: []valid.Project{{}},
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo set auto autodiscover with a project default disabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
projects: []valid.Project{{}},
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with no projects default enabled",
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with no projects default disabled",
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with no projects default auto",
|
||||
defaultAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with a project default enabled",
|
||||
projects: []valid.Project{{}},
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with a project default disabled",
|
||||
projects: []valid.Project{{}},
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with a project default auto",
|
||||
projects: []valid.Project{{}},
|
||||
defaultAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.description, func(t *testing.T) {
|
||||
r := valid.RepoCfg{
|
||||
Projects: c.projects,
|
||||
AutoDiscover: nil,
|
||||
}
|
||||
if c.repoAutoDiscover != "" {
|
||||
r.AutoDiscover = &valid.AutoDiscover{c.repoAutoDiscover}
|
||||
}
|
||||
enabled := r.AutoDiscoverEnabled(c.defaultAutoDiscover)
|
||||
Equals(t, c.expEnabled, enabled)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/runatlantis/atlantis/server/core/config"
|
||||
"github.com/runatlantis/atlantis/server/events/command"
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs"
|
||||
)
|
||||
|
||||
@@ -54,6 +55,7 @@ func NewInstrumentedProjectCommandBuilder(
|
||||
RestrictFileList bool,
|
||||
SilenceNoProjects bool,
|
||||
IncludeGitUntrackedFiles bool,
|
||||
AutoDiscoverMode string,
|
||||
scope tally.Scope,
|
||||
logger logging.SimpleLogging,
|
||||
terraformClient terraform.Client,
|
||||
@@ -85,6 +87,7 @@ func NewInstrumentedProjectCommandBuilder(
|
||||
RestrictFileList,
|
||||
SilenceNoProjects,
|
||||
IncludeGitUntrackedFiles,
|
||||
AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -114,6 +117,7 @@ func NewProjectCommandBuilder(
|
||||
RestrictFileList bool,
|
||||
SilenceNoProjects bool,
|
||||
IncludeGitUntrackedFiles bool,
|
||||
AutoDiscoverMode string,
|
||||
scope tally.Scope,
|
||||
logger logging.SimpleLogging,
|
||||
terraformClient terraform.Client,
|
||||
@@ -136,6 +140,7 @@ func NewProjectCommandBuilder(
|
||||
RestrictFileList: RestrictFileList,
|
||||
SilenceNoProjects: SilenceNoProjects,
|
||||
IncludeGitUntrackedFiles: IncludeGitUntrackedFiles,
|
||||
AutoDiscoverMode: AutoDiscoverMode,
|
||||
ProjectCommandContextBuilder: NewProjectCommandContextBuilder(
|
||||
policyChecksSupported,
|
||||
commentBuilder,
|
||||
@@ -242,6 +247,8 @@ type DefaultProjectCommandBuilder struct {
|
||||
SilenceNoProjects bool
|
||||
// User config option: Include git untracked files in the modified file list.
|
||||
IncludeGitUntrackedFiles bool
|
||||
// User config option: Controls auto-discovery of projects in a repository.
|
||||
AutoDiscoverMode string
|
||||
// Handles the actual running of Terraform commands.
|
||||
TerraformExecutor terraform.Client
|
||||
}
|
||||
@@ -336,6 +343,13 @@ func (p *DefaultProjectCommandBuilder) buildAllCommandsByCfg(ctx *command.Contex
|
||||
|
||||
ctx.Log.Debug("%d files were modified in this pull request. Modified files: %v", len(modifiedFiles), modifiedFiles)
|
||||
|
||||
// Get default AutoDiscoverMode from userConfig/globalConfig
|
||||
defaultAutoDiscoverMode := valid.AutoDiscoverMode(p.AutoDiscoverMode)
|
||||
globalAutoDiscover := p.GlobalCfg.RepoAutoDiscoverCfg(ctx.Pull.BaseRepo.ID())
|
||||
if globalAutoDiscover != nil {
|
||||
defaultAutoDiscoverMode = globalAutoDiscover.Mode
|
||||
}
|
||||
|
||||
if p.SkipCloneNoChanges && p.VCSClient.SupportsSingleFileDownload(ctx.Pull.BaseRepo) {
|
||||
repoCfgFile := p.GlobalCfg.RepoConfigFile(ctx.Pull.BaseRepo.ID())
|
||||
hasRepoCfg, repoCfgData, err := p.VCSClient.GetFileContent(ctx.Pull, repoCfgFile)
|
||||
@@ -349,18 +363,27 @@ func (p *DefaultProjectCommandBuilder) buildAllCommandsByCfg(ctx *command.Contex
|
||||
return nil, errors.Wrapf(err, "parsing %s", repoCfgFile)
|
||||
}
|
||||
ctx.Log.Info("successfully parsed remote %s file", repoCfgFile)
|
||||
if len(repoCfg.Projects) > 0 {
|
||||
matchingProjects, err := p.ProjectFinder.DetermineProjectsViaConfig(ctx.Log, modifiedFiles, repoCfg, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.Log.Info("%d projects are changed on MR %q based on their when_modified config", len(matchingProjects), ctx.Pull.Num)
|
||||
if len(matchingProjects) == 0 {
|
||||
ctx.Log.Info("skipping repo clone since no project was modified")
|
||||
return []command.ProjectContext{}, nil
|
||||
|
||||
if repoCfg.AutoDiscover != nil {
|
||||
defaultAutoDiscoverMode = repoCfg.AutoDiscover.Mode
|
||||
}
|
||||
// If auto discover is enabled, we never want to skip cloning
|
||||
if !repoCfg.AutoDiscoverEnabled(defaultAutoDiscoverMode) {
|
||||
if len(repoCfg.Projects) > 0 {
|
||||
matchingProjects, err := p.ProjectFinder.DetermineProjectsViaConfig(ctx.Log, modifiedFiles, repoCfg, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.Log.Info("%d projects are changed on MR %q based on their when_modified config", len(matchingProjects), ctx.Pull.Num)
|
||||
if len(matchingProjects) == 0 {
|
||||
ctx.Log.Info("skipping repo clone since no project was modified")
|
||||
return []command.ProjectContext{}, nil
|
||||
}
|
||||
} else {
|
||||
ctx.Log.Info("no projects are defined in %s. Will resume automatic detection", repoCfgFile)
|
||||
}
|
||||
} else {
|
||||
ctx.Log.Info("No projects are defined in %s. Will resume automatic detection", repoCfgFile)
|
||||
ctx.Log.Info("automatic project discovery enabled. Will resume automatic detection")
|
||||
}
|
||||
// NOTE: We discard this work here and end up doing it again after
|
||||
// cloning to ensure all the return values are set properly with
|
||||
@@ -402,6 +425,15 @@ func (p *DefaultProjectCommandBuilder) buildAllCommandsByCfg(ctx *command.Contex
|
||||
return nil, errors.Wrapf(err, "parsing %s", repoCfgFile)
|
||||
}
|
||||
ctx.Log.Info("successfully parsed %s file", repoCfgFile)
|
||||
// It's possible we've already set defaultAutoDiscoverMode
|
||||
// from the config file while checking whether we can skip
|
||||
// cloning. We still need to set it here in the case that
|
||||
// we were not able to check whether we can skip cloning
|
||||
// and thus were not able to previously fetch the repo
|
||||
// config.
|
||||
if repoCfg.AutoDiscover != nil {
|
||||
defaultAutoDiscoverMode = repoCfg.AutoDiscover.Mode
|
||||
}
|
||||
}
|
||||
|
||||
moduleInfo, err := FindModuleProjects(repoDir, p.AutoDetectModuleFiles)
|
||||
@@ -450,21 +482,46 @@ func (p *DefaultProjectCommandBuilder) buildAllCommandsByCfg(ctx *command.Contex
|
||||
parallelApply,
|
||||
parallelPlan,
|
||||
verbose,
|
||||
repoCfg.AbortOnExcecutionOrderFail,
|
||||
abortOnExcecutionOrderFail,
|
||||
p.TerraformExecutor,
|
||||
)...)
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if repoCfg.AutoDiscoverEnabled(defaultAutoDiscoverMode) {
|
||||
// If there is no config file or it specified no projects, then we'll plan each project that
|
||||
// our algorithm determines was modified.
|
||||
if hasRepoCfg {
|
||||
ctx.Log.Info("No projects are defined in %s. Will resume automatic detection", repoCfgFile)
|
||||
if len(repoCfg.Projects) == 0 {
|
||||
ctx.Log.Info("no projects are defined in %s. Will resume automatic detection", repoCfgFile)
|
||||
} else {
|
||||
ctx.Log.Info("automatic project discovery enabled. Will resume automatic detection")
|
||||
}
|
||||
} else {
|
||||
ctx.Log.Info("found no %s file", repoCfgFile)
|
||||
}
|
||||
// build a module index for projects that are explicitly included
|
||||
modifiedProjects := p.ProjectFinder.DetermineProjects(ctx.Log, modifiedFiles, ctx.Pull.BaseRepo.FullName, repoDir, p.AutoplanFileList, moduleInfo)
|
||||
ctx.Log.Info("automatically determined that there were %d projects modified in this pull request: %s", len(modifiedProjects), modifiedProjects)
|
||||
allModifiedProjects := p.ProjectFinder.DetermineProjects(
|
||||
ctx.Log, modifiedFiles, ctx.Pull.BaseRepo.FullName, repoDir, p.AutoplanFileList, moduleInfo)
|
||||
// If a project is already manually configured with the same dir as a discovered project, the manually configured
|
||||
// project should take precedence
|
||||
modifiedProjects := make([]models.Project, 0)
|
||||
configuredProjDirs := make(map[string]bool)
|
||||
// We compare against all configured projects instead of projects which match the modified files in case a
|
||||
// project is being specifically excluded (ex: when_modified doesn't match). We don't want to accidentally
|
||||
// "discover" it again.
|
||||
for _, configProj := range repoCfg.Projects {
|
||||
// Clean the path to make sure ./rel_path is equivalent to rel_path, etc
|
||||
configuredProjDirs[filepath.Clean(configProj.Dir)] = true
|
||||
}
|
||||
for _, mp := range allModifiedProjects {
|
||||
_, dirExists := configuredProjDirs[filepath.Clean(mp.Path)]
|
||||
if !dirExists {
|
||||
modifiedProjects = append(modifiedProjects, mp)
|
||||
}
|
||||
}
|
||||
ctx.Log.Info("automatically determined that there were %d additional projects modified in this pull request: %s",
|
||||
len(modifiedProjects), modifiedProjects)
|
||||
for _, mp := range modifiedProjects {
|
||||
ctx.Log.Debug("determining config for project at dir: %q", mp.Path)
|
||||
pWorkspace, err := p.ProjectFinder.DetermineWorkspaceFromHCL(ctx.Log, repoDir)
|
||||
|
||||
@@ -673,6 +673,7 @@ projects:
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"auto",
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -888,6 +889,7 @@ projects:
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"auto",
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1137,6 +1139,7 @@ workflows:
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"auto",
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1293,6 +1296,7 @@ projects:
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
"auto",
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1318,6 +1322,149 @@ projects:
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildProjectCmdCtx_AutoDiscoverRespectsRepoConfig(t *testing.T) {
|
||||
logger := logging.NewNoopLogger(t)
|
||||
cases := map[string]struct {
|
||||
globalCfg string
|
||||
repoCfg string
|
||||
modifiedFiles []string
|
||||
expLen int
|
||||
}{
|
||||
"autodiscover disabled": {
|
||||
globalCfg: `
|
||||
repos:
|
||||
- id: /.*/
|
||||
autodiscover:
|
||||
mode: disabled
|
||||
`,
|
||||
repoCfg: `
|
||||
version: 3
|
||||
automerge: true
|
||||
`,
|
||||
modifiedFiles: []string{"project1/main.tf", "project2/main.tf", "project3/main.tf"},
|
||||
expLen: 0,
|
||||
},
|
||||
"autodiscover auto": {
|
||||
globalCfg: `
|
||||
repos:
|
||||
- id: /.*/
|
||||
autodiscover:
|
||||
mode: auto
|
||||
`,
|
||||
repoCfg: `
|
||||
version: 3
|
||||
automerge: true
|
||||
projects:
|
||||
- dir: project1
|
||||
workspace: myworkspace
|
||||
`,
|
||||
modifiedFiles: []string{"project1/main.tf", "project2/main.tf", "project3/main.tf"},
|
||||
expLen: 1,
|
||||
},
|
||||
"autodiscover enabled": {
|
||||
globalCfg: `
|
||||
repos:
|
||||
- id: /.*/
|
||||
autodiscover:
|
||||
mode: enabled
|
||||
`,
|
||||
repoCfg: `
|
||||
version: 3
|
||||
automerge: true
|
||||
projects:
|
||||
- dir: project1
|
||||
workspace: myworkspace
|
||||
`,
|
||||
modifiedFiles: []string{"project1/main.tf", "project2/main.tf", "project3/main.tf"},
|
||||
expLen: 3,
|
||||
},
|
||||
}
|
||||
|
||||
for name, c := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
tmp := DirStructure(t, map[string]interface{}{
|
||||
"project1": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
"project2": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
"project3": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
})
|
||||
|
||||
workingDir := NewMockWorkingDir()
|
||||
When(workingDir.Clone(Any[models.Repo](), Any[models.PullRequest](), Any[string]())).ThenReturn(tmp, false, nil)
|
||||
vcsClient := vcsmocks.NewMockClient()
|
||||
When(vcsClient.GetModifiedFiles(Any[models.Repo](), Any[models.PullRequest]())).ThenReturn(c.modifiedFiles, nil)
|
||||
|
||||
// Write and parse the global config file.
|
||||
globalCfgPath := filepath.Join(tmp, "global.yaml")
|
||||
Ok(t, os.WriteFile(globalCfgPath, []byte(c.globalCfg), 0600))
|
||||
parser := &config.ParserValidator{}
|
||||
globalCfgArgs := valid.GlobalCfgArgs{
|
||||
AllowRepoCfg: false,
|
||||
MergeableReq: false,
|
||||
ApprovedReq: false,
|
||||
UnDivergedReq: false,
|
||||
}
|
||||
|
||||
globalCfg, err := parser.ParseGlobalCfg(globalCfgPath, valid.NewGlobalCfgFromArgs(globalCfgArgs))
|
||||
Ok(t, err)
|
||||
|
||||
if c.repoCfg != "" {
|
||||
Ok(t, os.WriteFile(filepath.Join(tmp, "atlantis.yaml"), []byte(c.repoCfg), 0600))
|
||||
}
|
||||
statsScope, _, _ := metrics.NewLoggingScope(logging.NewNoopLogger(t), "atlantis")
|
||||
|
||||
terraformClient := mocks.NewMockClient()
|
||||
|
||||
builder := NewProjectCommandBuilder(
|
||||
false,
|
||||
parser,
|
||||
&DefaultProjectFinder{},
|
||||
vcsClient,
|
||||
workingDir,
|
||||
NewDefaultWorkingDirLocker(),
|
||||
globalCfg,
|
||||
&DefaultPendingPlanFinder{},
|
||||
&CommentParser{ExecutableName: "atlantis"},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
"**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl",
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
"auto",
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
)
|
||||
|
||||
ctxs, err := builder.BuildPlanCommands(
|
||||
&command.Context{
|
||||
Log: logger,
|
||||
Scope: statsScope,
|
||||
},
|
||||
&CommentCommand{
|
||||
RepoRelDir: "",
|
||||
Flags: nil,
|
||||
Name: command.Plan,
|
||||
Verbose: false,
|
||||
},
|
||||
)
|
||||
Equals(t, c.expLen, len(ctxs))
|
||||
Ok(t, err)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mustVersion(v string) *version.Version {
|
||||
vers, err := version.NewVersion(v)
|
||||
if err != nil {
|
||||
|
||||
@@ -33,6 +33,7 @@ var defaultUserConfig = struct {
|
||||
RestrictFileList bool
|
||||
SilenceNoProjects bool
|
||||
IncludeGitUntrackedFiles bool
|
||||
AutoDiscoverMode string
|
||||
}{
|
||||
SkipCloneNoChanges: false,
|
||||
EnableRegExpCmd: false,
|
||||
@@ -44,6 +45,7 @@ var defaultUserConfig = struct {
|
||||
RestrictFileList: false,
|
||||
SilenceNoProjects: false,
|
||||
IncludeGitUntrackedFiles: true,
|
||||
AutoDiscoverMode: "auto",
|
||||
}
|
||||
|
||||
func TestDefaultProjectCommandBuilder_BuildAutoplanCommands(t *testing.T) {
|
||||
@@ -194,6 +196,7 @@ projects:
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -232,6 +235,7 @@ func TestDefaultProjectCommandBuilder_BuildSinglePlanApplyCommand(t *testing.T)
|
||||
ExpErr string
|
||||
ExpApplyReqs []string
|
||||
EnableAutoMergeUserCfg bool
|
||||
AutoDiscoverModeUserCfg string
|
||||
EnableParallelPlanUserCfg bool
|
||||
EnableParallelApplyUserCfg bool
|
||||
ExpAutoMerge bool
|
||||
@@ -550,6 +554,7 @@ projects:
|
||||
userConfig.RestrictFileList,
|
||||
c.Silenced,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
c.AutoDiscoverModeUserCfg,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -739,6 +744,7 @@ projects:
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -770,6 +776,7 @@ func TestDefaultProjectCommandBuilder_BuildPlanCommands(t *testing.T) {
|
||||
RepoRelDir string
|
||||
Workspace string
|
||||
Automerge bool
|
||||
AutoDiscover valid.AutoDiscover
|
||||
ExpParallelPlan bool
|
||||
ExpParallelApply bool
|
||||
}
|
||||
@@ -956,6 +963,67 @@ projects:
|
||||
},
|
||||
},
|
||||
},
|
||||
"follow autodiscover enabled config": {
|
||||
DirStructure: map[string]interface{}{
|
||||
"project1": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
"project2": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
"project3": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
},
|
||||
AtlantisYAML: `version: 3
|
||||
autodiscover:
|
||||
mode: enabled
|
||||
projects:
|
||||
- name: project1-custom-name
|
||||
dir: project1`,
|
||||
ModifiedFiles: []string{"project1/main.tf", "project2/main.tf"},
|
||||
// project2 is autodiscovered, whereas project1 is not
|
||||
Exp: []expCtxFields{
|
||||
{
|
||||
ProjectName: "project1-custom-name",
|
||||
RepoRelDir: "project1",
|
||||
Workspace: "default",
|
||||
},
|
||||
{
|
||||
ProjectName: "",
|
||||
RepoRelDir: "project2",
|
||||
Workspace: "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
"autodiscover enabled but project excluded by empty when_modified": {
|
||||
DirStructure: map[string]interface{}{
|
||||
"project1": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
"project2": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
"project3": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
},
|
||||
AtlantisYAML: `version: 3
|
||||
autodiscover:
|
||||
mode: enabled
|
||||
projects:
|
||||
- dir: project1
|
||||
autoplan:
|
||||
when_modified: []`,
|
||||
ModifiedFiles: []string{"project1/main.tf", "project2/main.tf"},
|
||||
Exp: []expCtxFields{
|
||||
{
|
||||
ProjectName: "",
|
||||
RepoRelDir: "project2",
|
||||
Workspace: "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
logger := logging.NewNoopLogger(t)
|
||||
@@ -1007,6 +1075,7 @@ projects:
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1021,7 +1090,7 @@ projects:
|
||||
RepoRelDir: "",
|
||||
Flags: nil,
|
||||
Name: command.Plan,
|
||||
Verbose: false,
|
||||
Verbose: true,
|
||||
Workspace: "",
|
||||
ProjectName: "",
|
||||
})
|
||||
@@ -1111,6 +1180,7 @@ func TestDefaultProjectCommandBuilder_BuildMultiApply(t *testing.T) {
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1206,6 +1276,7 @@ projects:
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1296,6 +1367,7 @@ func TestDefaultProjectCommandBuilder_EscapeArgs(t *testing.T) {
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1467,6 +1539,7 @@ projects:
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1523,6 +1596,17 @@ parallel_plan: true`,
|
||||
ExpectedClones: Once(),
|
||||
ModifiedFiles: []string{"README.md"},
|
||||
},
|
||||
{
|
||||
AtlantisYAML: `
|
||||
version: 3
|
||||
autodiscover:
|
||||
mode: enabled
|
||||
projects:
|
||||
- dir: dir1`,
|
||||
ExpectedCtxs: 0,
|
||||
ExpectedClones: Once(),
|
||||
ModifiedFiles: []string{"dir2/main.tf"},
|
||||
},
|
||||
}
|
||||
|
||||
userConfig := defaultUserConfig
|
||||
@@ -1568,6 +1652,7 @@ parallel_plan: true`,
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1638,6 +1723,7 @@ func TestDefaultProjectCommandBuilder_WithPolicyCheckEnabled_BuildAutoplanComman
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1730,6 +1816,7 @@ func TestDefaultProjectCommandBuilder_BuildVersionCommand(t *testing.T) {
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1861,6 +1948,7 @@ func TestDefaultProjectCommandBuilder_BuildPlanCommands_Single_With_RestrictFile
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
@@ -1972,6 +2060,7 @@ func TestDefaultProjectCommandBuilder_BuildPlanCommands_with_IncludeGitUntracked
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverMode,
|
||||
scope,
|
||||
logger,
|
||||
terraformClient,
|
||||
|
||||
@@ -597,6 +597,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
userConfig.RestrictFileList,
|
||||
userConfig.SilenceNoProjects,
|
||||
userConfig.IncludeGitUntrackedFiles,
|
||||
userConfig.AutoDiscoverModeFlag,
|
||||
statsScope,
|
||||
logger,
|
||||
terraformClient,
|
||||
|
||||
@@ -15,6 +15,7 @@ type UserConfig struct {
|
||||
AllowRepoConfig bool `mapstructure:"allow-repo-config"`
|
||||
AllowCommands string `mapstructure:"allow-commands"`
|
||||
AtlantisURL string `mapstructure:"atlantis-url"`
|
||||
AutoDiscoverModeFlag string `mapstructure:"autodiscover-mode"`
|
||||
Automerge bool `mapstructure:"automerge"`
|
||||
AutoplanFileList string `mapstructure:"autoplan-file-list"`
|
||||
AutoplanModules bool `mapstructure:"autoplan-modules"`
|
||||
|
||||
Reference in New Issue
Block a user