diff --git a/.circleci/config.yml b/.circleci/config.yml index a195e81b5c..4aec881cd2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ jobs: # that flag starts the download asynchronously so we'd have a race # condition. # renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp - TERRAFORM_VERSION: 1.6.4 + TERRAFORM_VERSION: 1.6.5 steps: - checkout - run: make build-service diff --git a/.github/labeler.yml b/.github/labeler.yml index 02dede4a12..8dedcc2704 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,35 +1,45 @@ build: - - 'Dockerfile*' +- changed-files: + - any-glob-to-any-file: 'Dockerfile*' dependencies: - - 'yarn.lock' - - 'go.*' +- changed-files: + - any-glob-to-any-file: 'yarn.lock' + - any-glob-to-any-file: 'go.*' docs: - - 'runatlantis.io/**/*.md' - - 'README.md' +- changed-files: + - any-glob-to-any-file: 'runatlantis.io/**/*.md' + - any-glob-to-any-file: 'README.md' github-actions: - - '.github/**' +- changed-files: + - any-glob-to-any-file: '.github/**' go: - - '**/*.go' +- changed-files: + - any-glob-to-any-file: '**/*.go' provider/azuredevops: - - 'server/**/*azuredevops*.go' +- changed-files: + - any-glob-to-any-file: 'server/**/*azuredevops*.go' provider/bitbucket: - - 'server/**/*bitbucket*.go' - - 'server/events/vcs/bitbucketcloud/*.go' - - 'server/events/vcs/bitbucketserver/*.go' +- changed-files: + - any-glob-to-any-file: 'server/**/*bitbucket*.go' + - any-glob-to-any-file: 'server/events/vcs/bitbucketcloud/*.go' + - any-glob-to-any-file: 'server/events/vcs/bitbucketserver/*.go' provider/github: - - 'server/**/*github*.go' +- changed-files: + - any-glob-to-any-file: 'server/**/*github*.go' provider/gitlab: - - 'server/**/*gitlab*.go' +- changed-files: + - any-glob-to-any-file: 'server/**/*gitlab*.go' website: - - 'runatlantis.io/.vuepress/**/*' - - 'package.json' - - 'yarn.lock' +- changed-files: + - any-glob-to-any-file: 'runatlantis.io/.vuepress/**/*' + - any-glob-to-any-file: 'package.json' + - any-glob-to-any-file: 'yarn.lock' diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 0ecde30411..aed089def0 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -16,7 +16,4 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-22.04 steps: - - uses: actions/labeler@v4 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - configuration-path: .github/labeler.yml + - uses: actions/labeler@v5 diff --git a/.node-version b/.node-version index f3f52b42d3..d5a159609d 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20.9.0 +20.10.0 diff --git a/Dockerfile b/Dockerfile index 9fa5c24547..a0b483c95b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 # what distro is the image being built for -ARG ALPINE_TAG=3.18.4 +ARG ALPINE_TAG=3.18.5 ARG DEBIAN_TAG=12.2-slim ARG DEFAULT_TERRAFORM_VERSION=1.6.3 @@ -8,7 +8,7 @@ ARG DEFAULT_CONFTEST_VERSION=0.46.0 # Stage 1: build artifact and download deps -FROM golang:1.21.4-alpine AS builder +FROM golang:1.21.5-alpine AS builder ARG ATLANTIS_VERSION=dev ENV ATLANTIS_VERSION=${ATLANTIS_VERSION} diff --git a/cmd/server.go b/cmd/server.go index 795d165276..93c698ffe8 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -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 { diff --git a/cmd/server_test.go b/cmd/server_test.go index 387acf3de0..07a189f723 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -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", diff --git a/go.mod b/go.mod index 031b1b4c73..444f63e195 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/go-ozzo/ozzo-validation v3.6.0+incompatible github.com/go-playground/validator/v10 v10.16.0 github.com/go-test/deep v1.1.0 - github.com/golang-jwt/jwt/v5 v5.1.0 + github.com/golang-jwt/jwt/v5 v5.2.0 github.com/google/go-github/v54 v54.0.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.4.0 @@ -21,7 +21,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/hashicorp/terraform-config-inspect v0.0.0-20230925220900-5a6f8d18746d + github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72 github.com/kr/pretty v0.3.1 github.com/mcdafydd/go-azuredevops v0.12.1 github.com/microcosm-cc/bluemonday v1.0.26 @@ -33,7 +33,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/redis/go-redis/v9 v9.3.0 github.com/remeh/sizedwaitgroup v1.0.0 - github.com/shurcooL/githubv4 v0.0.0-20230704064427-599ae7bbf278 + github.com/shurcooL/githubv4 v0.0.0-20231126234147-1cffa1f02456 github.com/slack-go/slack v0.12.3 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index b79233f7b5..ab1e2ab6c1 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,8 @@ github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncV github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU= -github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -266,8 +266,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= -github.com/hashicorp/terraform-config-inspect v0.0.0-20230925220900-5a6f8d18746d h1:g6kHlvZrFPFKeWRj5q/zyJA5gu7rlJGPf17h8hX7LHY= -github.com/hashicorp/terraform-config-inspect v0.0.0-20230925220900-5a6f8d18746d/go.mod h1:l8HcFPm9cQh6Q0KSWoYPiePqMvRFenybP1CH2MjKdlg= +github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72 h1:nZ5gGjbe5o7XUu1d7j+Y5Ztcxlp+yaumTKH9i0D3wlg= +github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72/go.mod h1:l8HcFPm9cQh6Q0KSWoYPiePqMvRFenybP1CH2MjKdlg= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -407,8 +407,8 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/githubv4 v0.0.0-20230704064427-599ae7bbf278 h1:kdEGVAV4sO46DPtb8k793jiecUEhaX9ixoIBt41HEGU= -github.com/shurcooL/githubv4 v0.0.0-20230704064427-599ae7bbf278/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= +github.com/shurcooL/githubv4 v0.0.0-20231126234147-1cffa1f02456 h1:6dExqsYngGEiixqa1vmtlUd+zbyISilg0Cf3GWVdeYM= +github.com/shurcooL/githubv4 v0.0.0-20231126234147-1cffa1f02456/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 h1:B1PEwpArrNp4dkQrfxh/abbBAOZBVp0ds+fBEOUOqOc= github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= diff --git a/package.json b/package.json index db3bc0957e..061b92ad1d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "license": "Apache-2.0", "devDependencies": { "@vuepress/plugin-docsearch": "2.0.0-beta.66", - "@vuepress/plugin-google-analytics": "2.0.0-beta.68", + "@vuepress/plugin-google-analytics": "2.0.0-rc.0", "vuepress": "2.0.0-beta.66" }, "scripts": { diff --git a/runatlantis.io/docs/repo-level-atlantis-yaml.md b/runatlantis.io/docs/repo-level-atlantis-yaml.md index 5484f26e34..777552c634 100644 --- a/runatlantis.io/docs/repo-level-atlantis-yaml.md +++ b/runatlantis.io/docs/repo-level-atlantis-yaml.md @@ -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) diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index f87ec83144..6781abb149 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -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="" + # or + ATLANTIS_AUTODISCOVER_MODE="" + ``` + 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 diff --git a/runatlantis.io/docs/server-side-repo-config.md b/runatlantis.io/docs/server-side-repo-config.md index 01d8a175aa..178d2afe56 100644 --- a/runatlantis.io/docs/server-side-repo-config.md +++ b/runatlantis.io/docs/server-side-repo-config.md @@ -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 diff --git a/server/controllers/events/events_controller_e2e_test.go b/server/controllers/events/events_controller_e2e_test.go index e56c2c4131..e1d2085293 100644 --- a/server/controllers/events/events_controller_e2e_test.go +++ b/server/controllers/events/events_controller_e2e_test.go @@ -1352,6 +1352,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers false, false, false, + "auto", statsScope, logger, terraformClient, diff --git a/server/core/config/parser_validator_test.go b/server/core/config/parser_validator_test.go index 53caf9e539..3b269695da 100644 --- a/server/core/config/parser_validator_test.go +++ b/server/core/config/parser_validator_test.go @@ -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{ diff --git a/server/core/config/raw/autodiscover.go b/server/core/config/raw/autodiscover.go new file mode 100644 index 0000000000..156128d271 --- /dev/null +++ b/server/core/config/raw/autodiscover.go @@ -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, + } +} diff --git a/server/core/config/raw/autodiscover_test.go b/server/core/config/raw/autodiscover_test.go new file mode 100644 index 0000000000..1485367fe8 --- /dev/null +++ b/server/core/config/raw/autodiscover_test.go @@ -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()) + }) + } +} diff --git a/server/core/config/raw/global_cfg.go b/server/core/config/raw/global_cfg.go index 92a9ec29b6..b795294239 100644 --- a/server/core/config/raw/global_cfg.go +++ b/server/core/config/raw/global_cfg.go @@ -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, } } diff --git a/server/core/config/raw/repo_cfg.go b/server/core/config/raw/repo_cfg.go index eb511b04aa..f3a688725d 100644 --- a/server/core/config/raw/repo_cfg.go +++ b/server/core/config/raw/repo_cfg.go @@ -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, diff --git a/server/core/config/raw/repo_cfg_test.go b/server/core/config/raw/repo_cfg_test.go index 7b11655c13..ff057548d0 100644 --- a/server/core/config/raw/repo_cfg_test.go +++ b/server/core/config/raw/repo_cfg_test.go @@ -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", diff --git a/server/core/config/valid/autodiscover.go b/server/core/config/valid/autodiscover.go new file mode 100644 index 0000000000..c131c3bffe --- /dev/null +++ b/server/core/config/valid/autodiscover.go @@ -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 +} diff --git a/server/core/config/valid/global_cfg.go b/server/core/config/valid/global_cfg.go index 8aab42f67b..0006e937c5 100644 --- a/server/core/config/valid/global_cfg.go +++ b/server/core/config/valid/global_cfg.go @@ -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 + } } } } diff --git a/server/core/config/valid/global_cfg_test.go b/server/core/config/valid/global_cfg_test.go index d778a9f12d..5c51893470 100644 --- a/server/core/config/valid/global_cfg_test.go +++ b/server/core/config/valid/global_cfg_test.go @@ -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{ diff --git a/server/core/config/valid/repo_cfg.go b/server/core/config/valid/repo_cfg.go index fe441f4d05..f0aed102ad 100644 --- a/server/core/config/valid/repo_cfg.go +++ b/server/core/config/valid/repo_cfg.go @@ -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 diff --git a/server/core/config/valid/repo_cfg_test.go b/server/core/config/valid/repo_cfg_test.go index bfacaa7f2f..9b94994f51 100644 --- a/server/core/config/valid/repo_cfg_test.go +++ b/server/core/config/valid/repo_cfg_test.go @@ -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) + }) + } +} diff --git a/server/events/project_command_builder.go b/server/events/project_command_builder.go index 2347c9072a..25f0e2c3e9 100644 --- a/server/events/project_command_builder.go +++ b/server/events/project_command_builder.go @@ -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) diff --git a/server/events/project_command_builder_internal_test.go b/server/events/project_command_builder_internal_test.go index e8804c5142..bd524dea90 100644 --- a/server/events/project_command_builder_internal_test.go +++ b/server/events/project_command_builder_internal_test.go @@ -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 { diff --git a/server/events/project_command_builder_test.go b/server/events/project_command_builder_test.go index 6a3e73798d..6c3217fd24 100644 --- a/server/events/project_command_builder_test.go +++ b/server/events/project_command_builder_test.go @@ -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, diff --git a/server/server.go b/server/server.go index d5c54e8e9e..adf74ed300 100644 --- a/server/server.go +++ b/server/server.go @@ -597,6 +597,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { userConfig.RestrictFileList, userConfig.SilenceNoProjects, userConfig.IncludeGitUntrackedFiles, + userConfig.AutoDiscoverModeFlag, statsScope, logger, terraformClient, diff --git a/server/user_config.go b/server/user_config.go index aa6c120e0f..ce57d18a2f 100644 --- a/server/user_config.go +++ b/server/user_config.go @@ -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"` diff --git a/testdrive/utils.go b/testdrive/utils.go index 623baea979..ff5740fbc1 100644 --- a/testdrive/utils.go +++ b/testdrive/utils.go @@ -35,7 +35,7 @@ import ( ) const hashicorpReleasesURL = "https://releases.hashicorp.com" -const terraformVersion = "1.6.4" // renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp +const terraformVersion = "1.6.5" // renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp const ngrokDownloadURL = "https://bin.equinox.io/c/4VmDzA7iaHb" const ngrokAPIURL = "localhost:41414" // We hope this isn't used. const atlantisPort = 4141 diff --git a/testing/Dockerfile b/testing/Dockerfile index 85ad099ec5..7a6b86e857 100644 --- a/testing/Dockerfile +++ b/testing/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21.4 +FROM golang:1.21.5 RUN apt-get update && apt-get --no-install-recommends -y install unzip \ && apt-get clean \ @@ -6,7 +6,7 @@ RUN apt-get update && apt-get --no-install-recommends -y install unzip \ # Install Terraform # renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp -ENV TERRAFORM_VERSION=1.6.4 +ENV TERRAFORM_VERSION=1.6.5 RUN case $(uname -m) in x86_64|amd64) ARCH="amd64" ;; aarch64|arm64|armv7l) ARCH="arm64" ;; esac && \ wget -nv -O terraform.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_${ARCH}.zip && \ mkdir -p /usr/local/bin/tf/versions/${TERRAFORM_VERSION} && \ @@ -16,7 +16,7 @@ RUN case $(uname -m) in x86_64|amd64) ARCH="amd64" ;; aarch64|arm64|armv7l) ARCH # Install conftest # renovate: datasource=github-releases depName=open-policy-agent/conftest -ENV CONFTEST_VERSION=0.46.0 +ENV CONFTEST_VERSION=0.47.0 SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN case $(uname -m) in x86_64|amd64) ARCH="x86_64" ;; aarch64|arm64|armv7l) ARCH="arm64" ;; esac && \ curl -LOs https://github.com/open-policy-agent/conftest/releases/download/v${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION}_Linux_${ARCH}.tar.gz && \ diff --git a/yarn.lock b/yarn.lock index 63228d77ea..fe8c392a23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -662,14 +662,14 @@ vue "^3.3.4" vue-router "^4.2.4" -"@vuepress/client@2.0.0-beta.68": - version "2.0.0-beta.68" - resolved "https://registry.yarnpkg.com/@vuepress/client/-/client-2.0.0-beta.68.tgz#9dd56575326b6f4344a0b14a38035250f28aab01" - integrity sha512-Y6amMnkPxpmn51vcgy5yzm3gpIaqZo4Pa8ItPFd7MW6GQy6HVZRNaV9ufzWRPOAedLHgpT4aVXomidvTMEKHVw== +"@vuepress/client@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/client/-/client-2.0.0-rc.0.tgz#851677f81d90ad4fb5d5fd4c5693c499b3dd0ad2" + integrity sha512-TwQx8hJgYONYxX+QltZ2aw9O5Ym6SKelfiUduuIRb555B1gece/jSVap3H/ZwyBhpgJMtG4+/Mrmf8nlDSHjvw== dependencies: "@vue/devtools-api" "^6.5.1" - "@vuepress/shared" "2.0.0-beta.68" - "@vueuse/core" "^10.6.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vueuse/core" "^10.6.1" vue "^3.3.8" vue-router "^4.2.5" @@ -684,15 +684,15 @@ "@vuepress/utils" "2.0.0-beta.66" vue "^3.3.4" -"@vuepress/core@2.0.0-beta.68": - version "2.0.0-beta.68" - resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-2.0.0-beta.68.tgz#d796b7802ab5a5f8876ee151a7bc3db98cf5ef90" - integrity sha512-/c+3gdduDyiyeGARzui6Z5ZeZurRGcbVSmqcUfb8SjB7sHojDt+bq/7gYeXKXrJ4R0zPpmqshlZdNGOSY4+uGQ== +"@vuepress/core@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-2.0.0-rc.0.tgz#b4a8f68a58e755ecc23d330c7d7769270ce792e4" + integrity sha512-uoOaZP1MdxZYJIAJcRcmYKKeCIVnxZeOuLMOOB9CPuAKSalT1RvJ1lztw6RX3q9SPnlqtSZPQXDncPAZivw4pA== dependencies: - "@vuepress/client" "2.0.0-beta.68" - "@vuepress/markdown" "2.0.0-beta.68" - "@vuepress/shared" "2.0.0-beta.68" - "@vuepress/utils" "2.0.0-beta.68" + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/markdown" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" vue "^3.3.8" "@vuepress/markdown@2.0.0-beta.66": @@ -717,10 +717,10 @@ markdown-it-emoji "^2.0.2" mdurl "^1.0.1" -"@vuepress/markdown@2.0.0-beta.68": - version "2.0.0-beta.68" - resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-2.0.0-beta.68.tgz#30df752af338cfb208ab8a1d008efca1d7bb9ae3" - integrity sha512-wQOVw1QQSnkdKClTnv3dHw1A7Y+XF2eu2hJmhTf9XOnEMxQ9taacIq5iRuQdcfR+Y8rjWmrzrqWZL+MiJbxKMQ== +"@vuepress/markdown@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-2.0.0-rc.0.tgz#513de09b470306e3a647ce2901995d0e63efbc0e" + integrity sha512-USmqdKKMT6ZFHYRztTjKUlO8qgGfnEygMAAq4AzC/uYXiEfrbMBLAWJhteyGS56P3rGLj0OPAhksE681bX/wOg== dependencies: "@mdit-vue/plugin-component" "^1.0.0" "@mdit-vue/plugin-frontmatter" "^1.0.0" @@ -732,8 +732,8 @@ "@mdit-vue/types" "^1.0.0" "@types/markdown-it" "^13.0.6" "@types/markdown-it-emoji" "^2.0.4" - "@vuepress/shared" "2.0.0-beta.68" - "@vuepress/utils" "2.0.0-beta.68" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" markdown-it "^13.0.2" markdown-it-anchor "^8.6.7" markdown-it-emoji "^2.0.2" @@ -813,14 +813,14 @@ "@vuepress/utils" "2.0.0-beta.66" execa "^7.1.1" -"@vuepress/plugin-google-analytics@2.0.0-beta.68": - version "2.0.0-beta.68" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.68.tgz#38e41837fdc4e4cfdaeca5528442cd68a74269e1" - integrity sha512-O8qqSp3Zo9GwwB7d4FnCxMwBpc4QxSro7wqHRYRwd0nHxPtnEQFZfYFqn8vO4qEBjrx0sevQeS8njN2O5b7xuA== +"@vuepress/plugin-google-analytics@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-google-analytics/-/plugin-google-analytics-2.0.0-rc.0.tgz#1ddc27e8f76dcd6e3fc278adaf943f13f4eac5c1" + integrity sha512-rkYW2LGkLAfRFtaFWVPr1V2mS6hwgYhn2hLeJAF5xHlC3PcjCiSV0cqH7ooeCo+FBJUlCtMQ9N8iSNl63vd7VQ== dependencies: - "@vuepress/client" "2.0.0-beta.68" - "@vuepress/core" "2.0.0-beta.68" - "@vuepress/utils" "2.0.0-beta.68" + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" "@vuepress/plugin-medium-zoom@2.0.0-beta.66": version "2.0.0-beta.66" @@ -881,10 +881,10 @@ "@mdit-vue/types" "^0.12.0" "@vue/shared" "^3.3.4" -"@vuepress/shared@2.0.0-beta.68": - version "2.0.0-beta.68" - resolved "https://registry.yarnpkg.com/@vuepress/shared/-/shared-2.0.0-beta.68.tgz#ea413dde17e3e8663d0b82749747246c6981319f" - integrity sha512-vnlOOchZ7ZHeTQuFDKcTC1AKF5zl4+XKwZZdpX9cUkIl3rYbM4y80yoWvfG5SQnPjjoYG57g4Qz21Fa8u/CnCQ== +"@vuepress/shared@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/shared/-/shared-2.0.0-rc.0.tgz#0d6ef42940dd030d575c877160fb0583791a9d6c" + integrity sha512-ikdSfjRv5LGM1iv4HHwF9P6gqTjaFCXKPK+hzlkHFHNZO1GLqk7/BPc4F51tAG1s8TcLhUZc+54LrfgS7PkXXA== dependencies: "@mdit-vue/types" "^1.0.0" "@vue/shared" "^3.3.8" @@ -931,15 +931,15 @@ picocolors "^1.0.0" upath "^2.0.1" -"@vuepress/utils@2.0.0-beta.68": - version "2.0.0-beta.68" - resolved "https://registry.yarnpkg.com/@vuepress/utils/-/utils-2.0.0-beta.68.tgz#9dbdf1cc5afd4c08a3a58924dfbf535fc13862a5" - integrity sha512-asRN+c8JCIVJWusP/V0FY8rgArGwuKXarEIKwFHcaR7x9IeB3Iww4p8raQHb1xYJADM7QFXx1gs2oM6Fx4XsUw== +"@vuepress/utils@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/utils/-/utils-2.0.0-rc.0.tgz#34331cea9d4f843cc0c5641aaa395ab49ee66041" + integrity sha512-Q1ay/woClDHcW0Qe91KsnHoupdNN0tp/vhjvVLuAYxlv/1Obii7hz9WFcajyyGEhmsYxdvG2sGmcxFA02tuKkw== dependencies: "@types/debug" "^4.1.12" "@types/fs-extra" "^11.0.4" "@types/hash-sum" "^1.0.2" - "@vuepress/shared" "2.0.0-beta.68" + "@vuepress/shared" "2.0.0-rc.0" debug "^4.3.4" fs-extra "^11.1.1" globby "^14.0.0" @@ -948,25 +948,25 @@ picocolors "^1.0.0" upath "^2.0.1" -"@vueuse/core@^10.2.1", "@vueuse/core@^10.6.0": - version "10.6.0" - resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.6.0.tgz#de4113cc63680d04a0c3e1e156992ca3e0d52115" - integrity sha512-+Yee+g9+9BEbvkyGdn4Bf4yZx9EfocAytpV2ZlrlP7xcz+qznLmZIDqDroTvc5vtMkWZicisgEv8dt3+jL+HQg== +"@vueuse/core@^10.2.1", "@vueuse/core@^10.6.1": + version "10.6.1" + resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.6.1.tgz#5b16d8238054c6983b6cb7cd77a78035f098dd89" + integrity sha512-Pc26IJbqgC9VG1u6VY/xrXXfxD33hnvxBnKrLlA2LJlyHII+BSrRoTPJgGYq7qZOu61itITFUnm6QbacwZ4H8Q== dependencies: "@types/web-bluetooth" "^0.0.20" - "@vueuse/metadata" "10.6.0" - "@vueuse/shared" "10.6.0" + "@vueuse/metadata" "10.6.1" + "@vueuse/shared" "10.6.1" vue-demi ">=0.14.6" -"@vueuse/metadata@10.6.0": - version "10.6.0" - resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.6.0.tgz#f652829668ba4146ab0f79d52ea26b5544f12fe8" - integrity sha512-mzKHkHoiK6xVz01VzQjM2l6ofUanEaofgEGPgDHcAzlvOTccPRTIdEuzneOUTYxgfm1vkDikS6rtrEw/NYlaTQ== +"@vueuse/metadata@10.6.1": + version "10.6.1" + resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.6.1.tgz#100faa0ced3c0ab4c014fb8e66e781e85e4eb88d" + integrity sha512-qhdwPI65Bgcj23e5lpGfQsxcy0bMjCAsUGoXkJ7DsoeDUdasbZ2DBa4dinFCOER3lF4gwUv+UD2AlA11zdzMFw== -"@vueuse/shared@10.6.0": - version "10.6.0" - resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.6.0.tgz#e13f3b0f642db6467bd8aadf9693409f33f45fc4" - integrity sha512-0t4MVE18sO+/4Gh0jfeOXBTjKeV4606N9kIrDOLPjFl8Rwnlodn+QC5A4LfJuysK7aOsTMjF3KnzNeueaI0xlQ== +"@vueuse/shared@10.6.1": + version "10.6.1" + resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.6.1.tgz#1d9fc1e3f9083e45b59a693fc372bc50ad62a9e4" + integrity sha512-TECVDTIedFlL0NUfHWncf3zF9Gc4VfdxfQc8JFwoVZQmxpONhLxFrlm0eHQeidHj4rdTPL3KXJa0TZCk1wnc5Q== dependencies: vue-demi ">=0.14.6"