mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-29 17:08:23 +00:00
* Add a branch regex to the repo.yaml config to allow Atlantis to
accept only pull requests with a given base branch (the branch
a PR would merge _into_).
* `branch` is optional. By default its unset and Atlantis will match
webhooks for pull requests for any branch (no change)
Match any PR,
```
repos:
- id: /.*/
branch: /.*/
```
Match only PRs with master or main base branch.
```
repos:
- id: /.*/
branch: /(main|master)/
```
Some repos have special pull request branching practices. For example,
plan/apply from feature branches merging into master, but separate flows
for merging master into a release branch (where Atlantis isn't used).
Adding a regex allows for flexible workflow options. For example, you
could have Atlantis ignore "release" branches.
1553 lines
35 KiB
Go
1553 lines
35 KiB
Go
package yaml_test
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/go-version"
|
|
"github.com/runatlantis/atlantis/server/events/yaml"
|
|
"github.com/runatlantis/atlantis/server/events/yaml/valid"
|
|
. "github.com/runatlantis/atlantis/testing"
|
|
)
|
|
|
|
var globalCfg = valid.NewGlobalCfg(true, false, false)
|
|
|
|
func TestHasRepoCfg_DirDoesNotExist(t *testing.T) {
|
|
r := yaml.ParserValidator{}
|
|
exists, err := r.HasRepoCfg("/not/exist")
|
|
Ok(t, err)
|
|
Equals(t, false, exists)
|
|
}
|
|
|
|
func TestHasRepoCfg_FileDoesNotExist(t *testing.T) {
|
|
tmpDir, cleanup := TempDir(t)
|
|
defer cleanup()
|
|
r := yaml.ParserValidator{}
|
|
exists, err := r.HasRepoCfg(tmpDir)
|
|
Ok(t, err)
|
|
Equals(t, false, exists)
|
|
}
|
|
|
|
func TestHasRepoCfg_InvalidFileExtension(t *testing.T) {
|
|
tmpDir, cleanup := TempDir(t)
|
|
defer cleanup()
|
|
_, err := os.Create(filepath.Join(tmpDir, "atlantis.yml"))
|
|
Ok(t, err)
|
|
|
|
r := yaml.ParserValidator{}
|
|
_, err = r.HasRepoCfg(tmpDir)
|
|
ErrContains(t, "found \"atlantis.yml\" as config file; rename using the .yaml extension - \"atlantis.yaml\"", err)
|
|
}
|
|
|
|
func TestParseRepoCfg_DirDoesNotExist(t *testing.T) {
|
|
r := yaml.ParserValidator{}
|
|
_, err := r.ParseRepoCfg("/not/exist", globalCfg, "")
|
|
Assert(t, os.IsNotExist(err), "exp not exist err")
|
|
}
|
|
|
|
func TestParseRepoCfg_FileDoesNotExist(t *testing.T) {
|
|
tmpDir, cleanup := TempDir(t)
|
|
defer cleanup()
|
|
r := yaml.ParserValidator{}
|
|
_, err := r.ParseRepoCfg(tmpDir, globalCfg, "")
|
|
Assert(t, os.IsNotExist(err), "exp not exist err")
|
|
}
|
|
|
|
func TestParseRepoCfg_BadPermissions(t *testing.T) {
|
|
tmpDir, cleanup := TempDir(t)
|
|
defer cleanup()
|
|
err := ioutil.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), nil, 0000)
|
|
Ok(t, err)
|
|
|
|
r := yaml.ParserValidator{}
|
|
_, err = r.ParseRepoCfg(tmpDir, globalCfg, "")
|
|
ErrContains(t, "unable to read atlantis.yaml file: ", err)
|
|
}
|
|
|
|
// Test both ParseRepoCfg and ParseGlobalCfg when given in valid YAML.
|
|
// We only have a few cases here because we assume the YAML library to be
|
|
// well tested. See https://github.com/go-yaml/yaml/blob/v2/decode_test.go#L810.
|
|
func TestParseCfgs_InvalidYAML(t *testing.T) {
|
|
cases := []struct {
|
|
description string
|
|
input string
|
|
expErr string
|
|
}{
|
|
{
|
|
"random characters",
|
|
"slkjds",
|
|
"yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `slkjds` into",
|
|
},
|
|
{
|
|
"just a colon",
|
|
":",
|
|
"yaml: did not find expected key",
|
|
},
|
|
}
|
|
|
|
tmpDir, cleanup := TempDir(t)
|
|
defer cleanup()
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.description, func(t *testing.T) {
|
|
confPath := filepath.Join(tmpDir, "atlantis.yaml")
|
|
err := ioutil.WriteFile(confPath, []byte(c.input), 0600)
|
|
Ok(t, err)
|
|
r := yaml.ParserValidator{}
|
|
_, err = r.ParseRepoCfg(tmpDir, globalCfg, "")
|
|
ErrContains(t, c.expErr, err)
|
|
_, err = r.ParseGlobalCfg(confPath, valid.NewGlobalCfg(false, false, false))
|
|
ErrContains(t, c.expErr, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseRepoCfg(t *testing.T) {
|
|
tfVersion, _ := version.NewVersion("v0.11.0")
|
|
cases := []struct {
|
|
description string
|
|
input string
|
|
expErr string
|
|
exp valid.RepoCfg
|
|
}{
|
|
// Version key.
|
|
{
|
|
description: "no version",
|
|
input: `
|
|
projects:
|
|
- dir: "."
|
|
`,
|
|
expErr: "version: is required. If you've just upgraded Atlantis you need to rewrite your atlantis.yaml for version 3. See www.runatlantis.io/docs/upgrading-atlantis-yaml.html.",
|
|
},
|
|
{
|
|
description: "unsupported version",
|
|
input: `
|
|
version: 0
|
|
projects:
|
|
- dir: "."
|
|
`,
|
|
expErr: "version: only versions 2 and 3 are supported.",
|
|
},
|
|
{
|
|
description: "empty version",
|
|
input: `
|
|
version:
|
|
projects:
|
|
- dir: "."
|
|
`,
|
|
expErr: "version: is required. If you've just upgraded Atlantis you need to rewrite your atlantis.yaml for version 3. See www.runatlantis.io/docs/upgrading-atlantis-yaml.html.",
|
|
},
|
|
{
|
|
description: "version 2",
|
|
input: `
|
|
version: 2
|
|
workflows:
|
|
custom:
|
|
plan:
|
|
steps:
|
|
- run: old 'shell parsing'
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 2,
|
|
Workflows: map[string]valid.Workflow{
|
|
"custom": {
|
|
Name: "custom",
|
|
Apply: valid.DefaultApplyStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "old shell parsing",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Projects key.
|
|
{
|
|
description: "empty projects list",
|
|
input: `
|
|
version: 3
|
|
projects:`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: nil,
|
|
Workflows: map[string]valid.Workflow{},
|
|
},
|
|
},
|
|
{
|
|
description: "project dir not set",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- `,
|
|
expErr: "projects: (0: (dir: cannot be blank.).).",
|
|
},
|
|
{
|
|
description: "project dir set",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: .`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
WorkflowName: nil,
|
|
TerraformVersion: nil,
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
ApplyRequirements: nil,
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{},
|
|
},
|
|
},
|
|
{
|
|
description: "autoplan should be enabled by default",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: make(map[string]valid.Workflow),
|
|
},
|
|
},
|
|
{
|
|
description: "autoplan should be enabled if only when_modified set",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
autoplan:
|
|
when_modified: ["**/*.tf*"]
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: make(map[string]valid.Workflow),
|
|
},
|
|
},
|
|
{
|
|
description: "if workflows not defined there are none",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: make(map[string]valid.Workflow),
|
|
},
|
|
},
|
|
{
|
|
description: "if workflows key set but with no workflows there are none",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
workflows: ~
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: make(map[string]valid.Workflow),
|
|
},
|
|
},
|
|
{
|
|
description: "if a plan or apply explicitly defines an empty steps key then it gets the defaults",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
workflows:
|
|
default:
|
|
plan:
|
|
steps:
|
|
apply:
|
|
steps:
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": {
|
|
Name: "default",
|
|
Plan: valid.DefaultPlanStage,
|
|
Apply: valid.DefaultApplyStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "project fields set except autoplan",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: .
|
|
workspace: myworkspace
|
|
terraform_version: v0.11.0
|
|
apply_requirements: [approved]
|
|
workflow: myworkflow
|
|
workflows:
|
|
myworkflow: ~`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "myworkspace",
|
|
WorkflowName: String("myworkflow"),
|
|
TerraformVersion: tfVersion,
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
ApplyRequirements: []string{"approved"},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"myworkflow": {
|
|
Name: "myworkflow",
|
|
Apply: valid.DefaultApplyStage,
|
|
Plan: valid.DefaultPlanStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "project field with autoplan",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: .
|
|
workspace: myworkspace
|
|
terraform_version: v0.11.0
|
|
apply_requirements: [approved]
|
|
workflow: myworkflow
|
|
autoplan:
|
|
enabled: false
|
|
workflows:
|
|
myworkflow: ~`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "myworkspace",
|
|
WorkflowName: String("myworkflow"),
|
|
TerraformVersion: tfVersion,
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: false,
|
|
},
|
|
ApplyRequirements: []string{"approved"},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"myworkflow": {
|
|
Name: "myworkflow",
|
|
Apply: valid.DefaultApplyStage,
|
|
Plan: valid.DefaultPlanStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "project field with mergeable apply requirement",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: .
|
|
workspace: myworkspace
|
|
terraform_version: v0.11.0
|
|
apply_requirements: [mergeable]
|
|
workflow: myworkflow
|
|
autoplan:
|
|
enabled: false
|
|
workflows:
|
|
myworkflow: ~`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "myworkspace",
|
|
WorkflowName: String("myworkflow"),
|
|
TerraformVersion: tfVersion,
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: false,
|
|
},
|
|
ApplyRequirements: []string{"mergeable"},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"myworkflow": {
|
|
Name: "myworkflow",
|
|
Apply: valid.DefaultApplyStage,
|
|
Plan: valid.DefaultPlanStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "project field with mergeable and approved apply requirements",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: .
|
|
workspace: myworkspace
|
|
terraform_version: v0.11.0
|
|
apply_requirements: [mergeable, approved]
|
|
workflow: myworkflow
|
|
autoplan:
|
|
enabled: false
|
|
workflows:
|
|
myworkflow: ~`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "myworkspace",
|
|
WorkflowName: String("myworkflow"),
|
|
TerraformVersion: tfVersion,
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: false,
|
|
},
|
|
ApplyRequirements: []string{"mergeable", "approved"},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"myworkflow": {
|
|
Name: "myworkflow",
|
|
Apply: valid.DefaultApplyStage,
|
|
Plan: valid.DefaultPlanStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "project dir with ..",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: ..`,
|
|
expErr: "projects: (0: (dir: cannot contain '..'.).).",
|
|
},
|
|
|
|
// Project must have dir set.
|
|
{
|
|
description: "project with no config",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
-`,
|
|
expErr: "projects: (0: (dir: cannot be blank.).).",
|
|
},
|
|
{
|
|
description: "project with no config at index 1",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
-`,
|
|
expErr: "projects: (1: (dir: cannot be blank.).).",
|
|
},
|
|
{
|
|
description: "project with unknown key",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- unknown: value`,
|
|
expErr: "yaml: unmarshal errors:\n line 4: field unknown not found in type raw.Project",
|
|
},
|
|
{
|
|
description: "referencing workflow that doesn't exist",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: .
|
|
workflow: undefined`,
|
|
expErr: "workflow \"undefined\" is not defined anywhere",
|
|
},
|
|
{
|
|
description: "two projects with same dir/workspace without names",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: .
|
|
workspace: workspace
|
|
- dir: .
|
|
workspace: workspace`,
|
|
expErr: "there are two or more projects with dir: \".\" workspace: \"workspace\" that are not all named; they must have a 'name' key so they can be targeted for apply's separately",
|
|
},
|
|
{
|
|
description: "two projects with same dir/workspace only one with name",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- name: myname
|
|
dir: .
|
|
workspace: workspace
|
|
- dir: .
|
|
workspace: workspace`,
|
|
expErr: "there are two or more projects with dir: \".\" workspace: \"workspace\" that are not all named; they must have a 'name' key so they can be targeted for apply's separately",
|
|
},
|
|
{
|
|
description: "two projects with same dir/workspace both with same name",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- name: myname
|
|
dir: .
|
|
workspace: workspace
|
|
- name: myname
|
|
dir: .
|
|
workspace: workspace`,
|
|
expErr: "found two or more projects with name \"myname\"; project names must be unique",
|
|
},
|
|
{
|
|
description: "two projects with same dir/workspace with different names",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- name: myname
|
|
dir: .
|
|
workspace: workspace
|
|
- name: myname2
|
|
dir: .
|
|
workspace: workspace`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Name: String("myname"),
|
|
Dir: ".",
|
|
Workspace: "workspace",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
{
|
|
Name: String("myname2"),
|
|
Dir: ".",
|
|
Workspace: "workspace",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{},
|
|
},
|
|
},
|
|
{
|
|
description: "if steps are set then we parse them properly",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
workflows:
|
|
default:
|
|
plan:
|
|
steps:
|
|
- init
|
|
- plan
|
|
policy_check:
|
|
steps:
|
|
- init
|
|
- policy_check
|
|
apply:
|
|
steps:
|
|
- plan # NOTE: we don't validate if they make sense
|
|
- apply
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": {
|
|
Name: "default",
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "init",
|
|
},
|
|
{
|
|
StepName: "plan",
|
|
},
|
|
},
|
|
},
|
|
PolicyCheck: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "init",
|
|
},
|
|
{
|
|
StepName: "policy_check",
|
|
},
|
|
},
|
|
},
|
|
Apply: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "plan",
|
|
},
|
|
{
|
|
StepName: "apply",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "we parse extra_args for the steps",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
workflows:
|
|
default:
|
|
plan:
|
|
steps:
|
|
- init:
|
|
extra_args: []
|
|
- plan:
|
|
extra_args:
|
|
- arg1
|
|
- arg2
|
|
policy_check:
|
|
steps:
|
|
- policy_check:
|
|
extra_args:
|
|
- arg1
|
|
apply:
|
|
steps:
|
|
- plan:
|
|
extra_args: [a, b]
|
|
- apply:
|
|
extra_args: ["a", "b"]
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": {
|
|
Name: "default",
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "init",
|
|
ExtraArgs: []string{},
|
|
},
|
|
{
|
|
StepName: "plan",
|
|
ExtraArgs: []string{"arg1", "arg2"},
|
|
},
|
|
},
|
|
},
|
|
PolicyCheck: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "policy_check",
|
|
ExtraArgs: []string{"arg1"},
|
|
},
|
|
},
|
|
},
|
|
Apply: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "plan",
|
|
ExtraArgs: []string{"a", "b"},
|
|
},
|
|
{
|
|
StepName: "apply",
|
|
ExtraArgs: []string{"a", "b"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "custom steps are parsed",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
workflows:
|
|
default:
|
|
plan:
|
|
steps:
|
|
- run: "echo \"plan hi\""
|
|
policy_check:
|
|
steps:
|
|
- run: "echo \"opa hi\""
|
|
apply:
|
|
steps:
|
|
- run: echo apply "arg 2"
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": {
|
|
Name: "default",
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "echo \"plan hi\"",
|
|
},
|
|
},
|
|
},
|
|
PolicyCheck: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "echo \"opa hi\"",
|
|
},
|
|
},
|
|
},
|
|
Apply: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "echo apply \"arg 2\"",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "env steps",
|
|
input: `
|
|
version: 3
|
|
projects:
|
|
- dir: "."
|
|
workflows:
|
|
default:
|
|
plan:
|
|
steps:
|
|
- env:
|
|
name: env_name
|
|
value: env_value
|
|
policy_check:
|
|
steps:
|
|
- env:
|
|
name: env_name
|
|
value: env_value
|
|
apply:
|
|
steps:
|
|
- env:
|
|
name: env_name
|
|
command: command and args
|
|
`,
|
|
exp: valid.RepoCfg{
|
|
Version: 3,
|
|
Projects: []valid.Project{
|
|
{
|
|
Dir: ".",
|
|
Workspace: "default",
|
|
Autoplan: valid.Autoplan{
|
|
WhenModified: []string{"**/*.tf*", "**/terragrunt.hcl"},
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": {
|
|
Name: "default",
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "env",
|
|
EnvVarName: "env_name",
|
|
EnvVarValue: "env_value",
|
|
},
|
|
},
|
|
},
|
|
PolicyCheck: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "env",
|
|
EnvVarName: "env_name",
|
|
EnvVarValue: "env_value",
|
|
},
|
|
},
|
|
},
|
|
Apply: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "env",
|
|
EnvVarName: "env_name",
|
|
RunCommand: "command and args",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
tmpDir, cleanup := TempDir(t)
|
|
defer cleanup()
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.description, func(t *testing.T) {
|
|
err := ioutil.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(c.input), 0600)
|
|
Ok(t, err)
|
|
|
|
r := yaml.ParserValidator{}
|
|
act, err := r.ParseRepoCfg(tmpDir, globalCfg, "")
|
|
if c.expErr != "" {
|
|
ErrEquals(t, c.expErr, err)
|
|
return
|
|
}
|
|
Ok(t, err)
|
|
Equals(t, c.exp, act)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test that we fail if the global validation fails. We test global validation
|
|
// more completely in GlobalCfg.ValidateRepoCfg().
|
|
func TestParseRepoCfg_GlobalValidation(t *testing.T) {
|
|
tmpDir, cleanup := TempDir(t)
|
|
defer cleanup()
|
|
|
|
repoCfg := `
|
|
version: 3
|
|
projects:
|
|
- dir: .
|
|
workflow: custom
|
|
workflows:
|
|
custom: ~`
|
|
err := ioutil.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(repoCfg), 0600)
|
|
Ok(t, err)
|
|
|
|
r := yaml.ParserValidator{}
|
|
_, err = r.ParseRepoCfg(tmpDir, valid.NewGlobalCfg(false, false, false), "repo_id")
|
|
ErrEquals(t, "repo config not allowed to set 'workflow' key: server-side config needs 'allowed_overrides: [workflow]'", err)
|
|
}
|
|
|
|
func TestParseGlobalCfg_NotExist(t *testing.T) {
|
|
r := yaml.ParserValidator{}
|
|
_, err := r.ParseGlobalCfg("/not/exist", valid.NewGlobalCfg(false, false, false))
|
|
ErrEquals(t, "unable to read /not/exist file: open /not/exist: no such file or directory", err)
|
|
}
|
|
|
|
func TestParseGlobalCfg(t *testing.T) {
|
|
defaultCfg := valid.NewGlobalCfg(false, false, false)
|
|
preWorkflowHook := &valid.PreWorkflowHook{
|
|
StepName: "run",
|
|
RunCommand: "custom workflow command",
|
|
}
|
|
preWorkflowHooks := []*valid.PreWorkflowHook{preWorkflowHook}
|
|
|
|
customWorkflow1 := valid.Workflow{
|
|
Name: "custom1",
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "custom command",
|
|
},
|
|
{
|
|
StepName: "init",
|
|
ExtraArgs: []string{"extra", "args"},
|
|
},
|
|
{
|
|
StepName: "plan",
|
|
},
|
|
},
|
|
},
|
|
PolicyCheck: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "custom command",
|
|
},
|
|
{
|
|
StepName: "plan",
|
|
ExtraArgs: []string{"extra", "args"},
|
|
},
|
|
{
|
|
StepName: "policy_check",
|
|
},
|
|
},
|
|
},
|
|
Apply: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "custom command",
|
|
},
|
|
{
|
|
StepName: "apply",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
conftestVersion, _ := version.NewVersion("v1.0.0")
|
|
|
|
cases := map[string]struct {
|
|
input string
|
|
expErr string
|
|
exp valid.GlobalCfg
|
|
}{
|
|
"empty file": {
|
|
input: "",
|
|
expErr: "file <tmp> was empty",
|
|
},
|
|
"invalid fields": {
|
|
input: "invalid: key",
|
|
expErr: "yaml: unmarshal errors:\n line 1: field invalid not found in type raw.GlobalCfg",
|
|
},
|
|
"no id specified": {
|
|
input: `repos:
|
|
- apply_requirements: []`,
|
|
expErr: "repos: (0: (id: cannot be blank.).).",
|
|
},
|
|
"invalid id regex": {
|
|
input: `repos:
|
|
- id: /?/`,
|
|
expErr: "repos: (0: (id: parsing: /?/: error parsing regexp: missing argument to repetition operator: `?`.).).",
|
|
},
|
|
"invalid branch regex": {
|
|
input: `repos:
|
|
- id: /.*/
|
|
branch: /?/`,
|
|
expErr: "repos: (0: (branch: parsing: /?/: error parsing regexp: missing argument to repetition operator: `?`.).).",
|
|
},
|
|
"workflow doesn't exist": {
|
|
input: `repos:
|
|
- id: /.*/
|
|
workflow: notdefined`,
|
|
expErr: "workflow \"notdefined\" is not defined",
|
|
},
|
|
"invalid allowed_override": {
|
|
input: `repos:
|
|
- id: /.*/
|
|
allowed_overrides: [invalid]`,
|
|
expErr: "repos: (0: (allowed_overrides: \"invalid\" is not a valid override, only \"apply_requirements\" and \"workflow\" are supported.).).",
|
|
},
|
|
"invalid apply_requirement": {
|
|
input: `repos:
|
|
- id: /.*/
|
|
apply_requirements: [invalid]`,
|
|
expErr: "repos: (0: (apply_requirements: \"invalid\" is not a valid apply_requirement, only \"approved\" and \"mergeable\" are supported.).).",
|
|
},
|
|
"no workflows key": {
|
|
input: `repos: []`,
|
|
exp: defaultCfg,
|
|
},
|
|
"workflows empty": {
|
|
input: `workflows:`,
|
|
exp: defaultCfg,
|
|
},
|
|
"workflow name but the rest is empty": {
|
|
input: `
|
|
workflows:
|
|
name:`,
|
|
exp: valid.GlobalCfg{
|
|
Repos: defaultCfg.Repos,
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": defaultCfg.Workflows["default"],
|
|
"name": {
|
|
Name: "name",
|
|
Apply: valid.DefaultApplyStage,
|
|
Plan: valid.DefaultPlanStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"workflow stages empty": {
|
|
input: `
|
|
workflows:
|
|
name:
|
|
apply:
|
|
plan:
|
|
`,
|
|
exp: valid.GlobalCfg{
|
|
Repos: defaultCfg.Repos,
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": defaultCfg.Workflows["default"],
|
|
"name": {
|
|
Name: "name",
|
|
Apply: valid.DefaultApplyStage,
|
|
Plan: valid.DefaultPlanStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"workflow steps empty": {
|
|
input: `
|
|
workflows:
|
|
name:
|
|
apply:
|
|
steps:
|
|
plan:
|
|
steps:`,
|
|
exp: valid.GlobalCfg{
|
|
Repos: defaultCfg.Repos,
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": defaultCfg.Workflows["default"],
|
|
"name": {
|
|
Name: "name",
|
|
Plan: valid.DefaultPlanStage,
|
|
PolicyCheck: valid.DefaultPolicyCheckStage,
|
|
Apply: valid.DefaultApplyStage,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"all keys specified": {
|
|
input: `
|
|
repos:
|
|
- id: github.com/owner/repo
|
|
|
|
apply_requirements: [approved, mergeable]
|
|
pre_workflow_hooks:
|
|
- run: custom workflow command
|
|
workflow: custom1
|
|
allowed_overrides: [apply_requirements, workflow]
|
|
allow_custom_workflows: true
|
|
- id: /.*/
|
|
branch: /(master|main)/
|
|
pre_workflow_hooks:
|
|
- run: custom workflow command
|
|
workflows:
|
|
custom1:
|
|
plan:
|
|
steps:
|
|
- run: custom command
|
|
- init:
|
|
extra_args: [extra, args]
|
|
- plan
|
|
policy_check:
|
|
steps:
|
|
- run: custom command
|
|
- plan:
|
|
extra_args: [extra, args]
|
|
- policy_check
|
|
apply:
|
|
steps:
|
|
- run: custom command
|
|
- apply
|
|
policies:
|
|
conftest_version: v1.0.0
|
|
policy_sets:
|
|
- name: good-policy
|
|
path: rel/path/to/policy
|
|
source: local
|
|
`,
|
|
exp: valid.GlobalCfg{
|
|
Repos: []valid.Repo{
|
|
defaultCfg.Repos[0],
|
|
{
|
|
ID: "github.com/owner/repo",
|
|
ApplyRequirements: []string{"approved", "mergeable"},
|
|
PreWorkflowHooks: preWorkflowHooks,
|
|
Workflow: &customWorkflow1,
|
|
AllowedOverrides: []string{"apply_requirements", "workflow"},
|
|
AllowCustomWorkflows: Bool(true),
|
|
},
|
|
{
|
|
IDRegex: regexp.MustCompile(".*"),
|
|
BranchRegex: regexp.MustCompile("(master|main)"),
|
|
PreWorkflowHooks: preWorkflowHooks,
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": defaultCfg.Workflows["default"],
|
|
"custom1": customWorkflow1,
|
|
},
|
|
PolicySets: valid.PolicySets{
|
|
Version: conftestVersion,
|
|
PolicySets: []valid.PolicySet{
|
|
{
|
|
Name: "good-policy",
|
|
Path: "rel/path/to/policy",
|
|
Source: valid.LocalPolicySet,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"id regex with trailing slash": {
|
|
input: `
|
|
repos:
|
|
- id: /github.com//
|
|
`,
|
|
exp: valid.GlobalCfg{
|
|
Repos: []valid.Repo{
|
|
defaultCfg.Repos[0],
|
|
{
|
|
IDRegex: regexp.MustCompile("github.com/"),
|
|
PreWorkflowHooks: []*valid.PreWorkflowHook{},
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": defaultCfg.Workflows["default"],
|
|
},
|
|
},
|
|
},
|
|
"referencing default workflow": {
|
|
input: `
|
|
repos:
|
|
- id: github.com/owner/repo
|
|
workflow: default
|
|
`,
|
|
exp: valid.GlobalCfg{
|
|
Repos: []valid.Repo{
|
|
defaultCfg.Repos[0],
|
|
{
|
|
ID: "github.com/owner/repo",
|
|
PreWorkflowHooks: []*valid.PreWorkflowHook{},
|
|
Workflow: defaultCfg.Repos[0].Workflow,
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": defaultCfg.Workflows["default"],
|
|
},
|
|
},
|
|
},
|
|
"redefine default workflow": {
|
|
input: `
|
|
workflows:
|
|
default:
|
|
plan:
|
|
steps:
|
|
- run: custom
|
|
policy_check:
|
|
steps: []
|
|
apply:
|
|
steps: []
|
|
`,
|
|
exp: valid.GlobalCfg{
|
|
Repos: []valid.Repo{
|
|
{
|
|
IDRegex: regexp.MustCompile(".*"),
|
|
BranchRegex: regexp.MustCompile(".*"),
|
|
PreWorkflowHooks: []*valid.PreWorkflowHook{},
|
|
ApplyRequirements: []string{},
|
|
Workflow: &valid.Workflow{
|
|
Name: "default",
|
|
Apply: valid.Stage{
|
|
Steps: nil,
|
|
},
|
|
PolicyCheck: valid.Stage{
|
|
Steps: nil,
|
|
},
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "custom",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
AllowedWorkflows: []string{},
|
|
AllowedOverrides: []string{},
|
|
AllowCustomWorkflows: Bool(false),
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": {
|
|
Name: "default",
|
|
Apply: valid.Stage{
|
|
Steps: nil,
|
|
},
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "custom",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, c := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
r := yaml.ParserValidator{}
|
|
tmp, cleanup := TempDir(t)
|
|
defer cleanup()
|
|
path := filepath.Join(tmp, "conf.yaml")
|
|
Ok(t, ioutil.WriteFile(path, []byte(c.input), 0600))
|
|
|
|
act, err := r.ParseGlobalCfg(path, valid.NewGlobalCfg(false, false, false))
|
|
|
|
if c.expErr != "" {
|
|
expErr := strings.Replace(c.expErr, "<tmp>", path, -1)
|
|
ErrEquals(t, expErr, err)
|
|
return
|
|
}
|
|
Ok(t, err)
|
|
|
|
if !act.PolicySets.HasPolicies() {
|
|
c.exp.PolicySets = act.PolicySets
|
|
}
|
|
|
|
Equals(t, c.exp, act)
|
|
// Have to hand-compare regexes because Equals doesn't do it.
|
|
for i, actRepo := range act.Repos {
|
|
expRepo := c.exp.Repos[i]
|
|
if expRepo.IDRegex != nil {
|
|
Assert(t, expRepo.IDRegex.String() == actRepo.IDRegex.String(),
|
|
"%q != %q for repos[%d]", expRepo.IDRegex.String(), actRepo.IDRegex.String(), i)
|
|
}
|
|
if expRepo.BranchRegex != nil {
|
|
Assert(t, expRepo.BranchRegex.String() == actRepo.BranchRegex.String(),
|
|
"%q != %q for repos[%d]", expRepo.BranchRegex.String(), actRepo.BranchRegex.String(), i)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test that if we pass in JSON strings everything should parse fine.
|
|
func TestParserValidator_ParseGlobalCfgJSON(t *testing.T) {
|
|
customWorkflow := valid.Workflow{
|
|
Name: "custom",
|
|
Plan: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "init",
|
|
},
|
|
{
|
|
StepName: "plan",
|
|
ExtraArgs: []string{"extra", "args"},
|
|
},
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "custom plan",
|
|
},
|
|
},
|
|
},
|
|
PolicyCheck: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "plan",
|
|
},
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "custom policy_check",
|
|
},
|
|
},
|
|
},
|
|
Apply: valid.Stage{
|
|
Steps: []valid.Step{
|
|
{
|
|
StepName: "run",
|
|
RunCommand: "my custom command",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
conftestVersion, _ := version.NewVersion("v1.0.0")
|
|
|
|
cases := map[string]struct {
|
|
json string
|
|
exp valid.GlobalCfg
|
|
expErr string
|
|
}{
|
|
"empty string": {
|
|
json: "",
|
|
expErr: "unexpected end of JSON input",
|
|
},
|
|
"empty object": {
|
|
json: "{}",
|
|
exp: valid.NewGlobalCfg(false, false, false),
|
|
},
|
|
"setting all keys": {
|
|
json: `
|
|
{
|
|
"repos": [
|
|
{
|
|
"id": "/.*/",
|
|
"workflow": "custom",
|
|
"allowed_workflows": ["custom"],
|
|
"apply_requirements": ["mergeable", "approved"],
|
|
"allowed_overrides": ["workflow", "apply_requirements"],
|
|
"allow_custom_workflows": true
|
|
},
|
|
{
|
|
"id": "github.com/owner/repo"
|
|
}
|
|
],
|
|
"workflows": {
|
|
"custom": {
|
|
"plan": {
|
|
"steps": [
|
|
"init",
|
|
{"plan": {"extra_args": ["extra", "args"]}},
|
|
{"run": "custom plan"}
|
|
]
|
|
},
|
|
"policy_check": {
|
|
"steps": [
|
|
"plan",
|
|
{"run": "custom policy_check"}
|
|
]
|
|
},
|
|
"apply": {
|
|
"steps": [
|
|
{"run": "my custom command"}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"policies": {
|
|
"conftest_version": "v1.0.0",
|
|
"policy_sets": [
|
|
{
|
|
"name": "good-policy",
|
|
"source": "local",
|
|
"path": "rel/path/to/policy"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
`,
|
|
exp: valid.GlobalCfg{
|
|
Repos: []valid.Repo{
|
|
valid.NewGlobalCfg(false, false, false).Repos[0],
|
|
{
|
|
IDRegex: regexp.MustCompile(".*"),
|
|
ApplyRequirements: []string{"mergeable", "approved"},
|
|
PreWorkflowHooks: []*valid.PreWorkflowHook{},
|
|
Workflow: &customWorkflow,
|
|
AllowedWorkflows: []string{"custom"},
|
|
AllowedOverrides: []string{"workflow", "apply_requirements"},
|
|
AllowCustomWorkflows: Bool(true),
|
|
},
|
|
{
|
|
ID: "github.com/owner/repo",
|
|
IDRegex: nil,
|
|
PreWorkflowHooks: []*valid.PreWorkflowHook{},
|
|
ApplyRequirements: nil,
|
|
AllowedOverrides: nil,
|
|
AllowCustomWorkflows: nil,
|
|
},
|
|
},
|
|
Workflows: map[string]valid.Workflow{
|
|
"default": valid.NewGlobalCfg(false, false, false).Workflows["default"],
|
|
"custom": customWorkflow,
|
|
},
|
|
PolicySets: valid.PolicySets{
|
|
Version: conftestVersion,
|
|
PolicySets: []valid.PolicySet{
|
|
{
|
|
Name: "good-policy",
|
|
Path: "rel/path/to/policy",
|
|
Source: valid.LocalPolicySet,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for name, c := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
pv := &yaml.ParserValidator{}
|
|
cfg, err := pv.ParseGlobalCfgJSON(c.json, valid.NewGlobalCfg(false, false, false))
|
|
if c.expErr != "" {
|
|
ErrEquals(t, c.expErr, err)
|
|
return
|
|
}
|
|
Ok(t, err)
|
|
|
|
if !cfg.PolicySets.HasPolicies() {
|
|
c.exp.PolicySets = cfg.PolicySets
|
|
}
|
|
|
|
Equals(t, c.exp, cfg)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test legacy shell parsing vs v3 parsing.
|
|
func TestParseRepoCfg_V2ShellParsing(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
expV2 string
|
|
expV2Err string
|
|
}{
|
|
{
|
|
in: "echo a b",
|
|
expV2: "echo a b",
|
|
},
|
|
{
|
|
in: "echo 'a b'",
|
|
expV2: "echo a b",
|
|
},
|
|
{
|
|
in: "echo 'a b",
|
|
expV2Err: "unable to parse \"echo 'a b\": EOF found when expecting closing quote.",
|
|
},
|
|
{
|
|
in: `mkdir a/b/c || printf \'your main.tf file does not provide default region.\\ncheck\'`,
|
|
expV2: `mkdir a/b/c || printf 'your main.tf file does not provide default region.\ncheck'`,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.in, func(t *testing.T) {
|
|
v2Dir, cleanup2 := TempDir(t)
|
|
defer cleanup2()
|
|
v3Dir, cleanup3 := TempDir(t)
|
|
defer cleanup3()
|
|
v2Path := filepath.Join(v2Dir, "atlantis.yaml")
|
|
v3Path := filepath.Join(v3Dir, "atlantis.yaml")
|
|
cfg := fmt.Sprintf(`workflows:
|
|
custom:
|
|
plan:
|
|
steps:
|
|
- run: %s
|
|
apply:
|
|
steps:
|
|
- run: %s`, c.in, c.in)
|
|
Ok(t, ioutil.WriteFile(v2Path, []byte("version: 2\n"+cfg), 0600))
|
|
Ok(t, ioutil.WriteFile(v3Path, []byte("version: 3\n"+cfg), 0600))
|
|
|
|
p := &yaml.ParserValidator{}
|
|
v2Cfg, err := p.ParseRepoCfg(v2Dir, valid.NewGlobalCfg(true, false, false), "")
|
|
if c.expV2Err != "" {
|
|
ErrEquals(t, c.expV2Err, err)
|
|
} else {
|
|
Ok(t, err)
|
|
Equals(t, c.expV2, v2Cfg.Workflows["custom"].Plan.Steps[0].RunCommand)
|
|
Equals(t, c.expV2, v2Cfg.Workflows["custom"].Apply.Steps[0].RunCommand)
|
|
}
|
|
|
|
v3Cfg, err := p.ParseRepoCfg(v3Dir, valid.NewGlobalCfg(true, false, false), "")
|
|
Ok(t, err)
|
|
Equals(t, c.in, v3Cfg.Workflows["custom"].Plan.Steps[0].RunCommand)
|
|
Equals(t, c.in, v3Cfg.Workflows["custom"].Apply.Steps[0].RunCommand)
|
|
})
|
|
}
|
|
}
|
|
|
|
// String is a helper routine that allocates a new string value
|
|
// to store v and returns a pointer to it.
|
|
func String(v string) *string { return &v }
|
|
|
|
// Bool is a helper routine that allocates a new bool value
|
|
// to store v and returns a pointer to it.
|
|
func Bool(v bool) *bool { return &v }
|