mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-28 23:58:19 +00:00
feat: set the shell for workflow command steps (#5024)
Signed-off-by: anryko <andrej.svenke@deliveryhero.com>
This commit is contained in:
@@ -599,6 +599,10 @@ Full
|
||||
```yaml
|
||||
- run:
|
||||
command: custom-command arg1 arg2
|
||||
shell: sh
|
||||
shellArgs:
|
||||
- "--debug"
|
||||
- "-c"
|
||||
output: show
|
||||
```
|
||||
|
||||
@@ -606,6 +610,8 @@ Full
|
||||
|-----|--------------------------------------------------------------|---------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| run | map\[string -> string\] | none | no | Run a custom command |
|
||||
| run.command | string | none | yes | Shell command to run |
|
||||
| run.shell | string | "sh" | no | Name of the shell to use for command execution |
|
||||
| run.shellArgs | string or []string | "-c" | no | Command line arguments to be passed to the shell. Cannot be set without `shell` |
|
||||
| run.output | string | "show" | no | How to post-process the output of this command when posted in the PR comment. The options are<br/>*`show` - preserve the full output<br/>* `hide` - hide output from comment (still visible in the real-time streaming output)<br/> * `strip_refreshing` - hide all output up until and including the last line containing "Refreshing...". This matches the behavior of the built-in `plan` command |
|
||||
|
||||
#### Native Environment Variables
|
||||
@@ -664,6 +670,13 @@ as the environment variable value.
|
||||
- env:
|
||||
name: ENV_NAME_2
|
||||
command: 'echo "dynamic-value-$(date)"'
|
||||
- env:
|
||||
name: ENV_NAME_3
|
||||
command: echo ${DIR%$REPO_REL_DIR}
|
||||
shell: bash
|
||||
shellArgs:
|
||||
- "--verbose"
|
||||
- "-c"
|
||||
```
|
||||
|
||||
| Key | Type | Default | Required | Description |
|
||||
@@ -672,6 +685,8 @@ as the environment variable value.
|
||||
| env.name | string | none | yes | Name of the environment variable |
|
||||
| env.value | string | none | no | Set the value of the environment variable to a hard-coded string. Cannot be set at the same time as `command` |
|
||||
| env.command | string | none | no | Set the value of the environment variable to the output of a command. Cannot be set at the same time as `value` |
|
||||
| env.shell | string | "sh" | no | Name of the shell to use for command execution. Cannot be set without `command` |
|
||||
| env.shellArgs | string or []string | "-c" | no | Command line arguments to be passed to the shell. Cannot be set without `shell` |
|
||||
|
||||
::: tip Notes
|
||||
|
||||
@@ -699,14 +714,20 @@ Full:
|
||||
```yaml
|
||||
- multienv:
|
||||
command: custom-command
|
||||
shell: bash
|
||||
shellArgs:
|
||||
- "--verbose"
|
||||
- "-c"
|
||||
output: show
|
||||
```
|
||||
|
||||
| Key | Type | Default | Required | Description |
|
||||
|------------------|-----------------------|---------|----------|-------------------------------------------------------------------------------------|
|
||||
| multienv | map[string -> string] | none | no | Run a custom command and add printed environment variables |
|
||||
| multienv.command | string | none | yes | Name of the custom script to run |
|
||||
| multienv.output | string | "show" | no | Setting output to "hide" will supress the message obout added environment variables |
|
||||
| Key | Type | Default | Required | Description |
|
||||
|--------------------|-----------------------|---------|----------|-------------------------------------------------------------------------------------|
|
||||
| multienv | map[string -> string] | none | no | Run a custom command and add printed environment variables |
|
||||
| multienv.command | string | none | yes | Name of the custom script to run |
|
||||
| multienv.shell | string | "sh" | no | Name of the shell to use for command execution |
|
||||
| multienv.shellArgs | string or []string | "-c" | no | Command line arguments to be passed to the shell. Cannot be set without `shell` |
|
||||
| multienv.output | string | "show" | no | Setting output to "hide" will supress the message obout added environment variables |
|
||||
|
||||
The output of the command execution must have the following format:
|
||||
`EnvVar1Name=value1,EnvVar2Name=value2,EnvVar3Name=value3`
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation"
|
||||
"github.com/runatlantis/atlantis/server/core/config/valid"
|
||||
"github.com/runatlantis/atlantis/server/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -27,45 +28,58 @@ const (
|
||||
MultiEnvStepName = "multienv"
|
||||
ImportStepName = "import"
|
||||
StateRmStepName = "state_rm"
|
||||
ShellArgKey = "shell"
|
||||
ShellArgsArgKey = "shellArgs"
|
||||
)
|
||||
|
||||
// Step represents a single action/command to perform. In YAML, it can be set as
|
||||
// 1. A single string for a built-in command:
|
||||
// - init
|
||||
// - plan
|
||||
// - policy_check
|
||||
//
|
||||
// 2. A map for an env step with name and command or value, or a run step with a command and output config
|
||||
// - env:
|
||||
// name: test
|
||||
// command: echo 312
|
||||
// value: value
|
||||
// - multienv:
|
||||
// command: envs.sh
|
||||
// outpiut: hide
|
||||
// - run:
|
||||
// command: my custom command
|
||||
// output: hide
|
||||
//
|
||||
// 3. A map for a built-in command and extra_args:
|
||||
// - plan:
|
||||
// extra_args: [-var-file=staging.tfvars]
|
||||
//
|
||||
// 4. A map for a custom run command:
|
||||
// - run: my custom command
|
||||
//
|
||||
// Here we parse step in the most generic fashion possible. See fields for more
|
||||
// details.
|
||||
/*
|
||||
Step represents a single action/command to perform. In YAML, it can be set as
|
||||
1. A single string for a built-in command:
|
||||
- init
|
||||
- plan
|
||||
- policy_check
|
||||
|
||||
2. A map for an env step with name and command or value, or a run step with a command and output config
|
||||
- env:
|
||||
name: test_command
|
||||
command: echo 312
|
||||
- env:
|
||||
name: test_value
|
||||
value: value
|
||||
- env:
|
||||
name: test_bash_command
|
||||
command: echo ${test_value::7}
|
||||
shell: bash
|
||||
shellArgs: ["--verbose", "-c"]
|
||||
- multienv:
|
||||
command: envs.sh
|
||||
output: hide
|
||||
shell: sh
|
||||
shellArgs: -c
|
||||
- run:
|
||||
command: my custom command
|
||||
output: hide
|
||||
|
||||
3. A map for a built-in command and extra_args:
|
||||
- plan:
|
||||
extra_args: [-var-file=staging.tfvars]
|
||||
|
||||
4. A map for a custom run command:
|
||||
- run: my custom command
|
||||
|
||||
Here we parse step in the most generic fashion possible. See fields for more
|
||||
details.
|
||||
*/
|
||||
type Step struct {
|
||||
// Key will be set in case #1 and #3 above to the key. In case #2, there
|
||||
// could be multiple keys (since the element is a map) so we don't set Key.
|
||||
Key *string
|
||||
// CommandMap will be set in case #2 above.
|
||||
CommandMap map[string]map[string]string
|
||||
// Map will be set in case #3 above.
|
||||
Map map[string]map[string][]string
|
||||
// StringVal will be set in case #4 above.
|
||||
StringVal map[string]string
|
||||
// Map will be set in case #3 above.
|
||||
Map map[string]map[string][]string
|
||||
// CommandMap will be set in case #2 above.
|
||||
CommandMap map[string]map[string]interface{}
|
||||
}
|
||||
|
||||
func (s *Step) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
@@ -142,7 +156,8 @@ func (s Step) Validate() error {
|
||||
}
|
||||
for k := range args {
|
||||
if k != ExtraArgsKey {
|
||||
return fmt.Errorf("built-in steps only support a single %s key, found %q in step %s", ExtraArgsKey, k, stepName)
|
||||
return fmt.Errorf("built-in steps only support a single %s key, found %q in step %s",
|
||||
ExtraArgsKey, k, stepName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +165,7 @@ func (s Step) Validate() error {
|
||||
}
|
||||
|
||||
envOrRunOrMultiEnvStep := func(value interface{}) error {
|
||||
elem := value.(map[string]map[string]string)
|
||||
elem := value.(map[string]map[string]interface{})
|
||||
var keys []string
|
||||
for k := range elem {
|
||||
keys = append(keys, k)
|
||||
@@ -169,63 +184,100 @@ func (s Step) Validate() error {
|
||||
stepName := keys[0]
|
||||
args := elem[keys[0]]
|
||||
|
||||
var argKeys []string
|
||||
for k := range args {
|
||||
argKeys = append(argKeys, k)
|
||||
}
|
||||
argMap := make(map[string]interface{})
|
||||
for k, v := range args {
|
||||
argMap[k] = v
|
||||
}
|
||||
// Sort so tests can be deterministic.
|
||||
sort.Strings(argKeys)
|
||||
|
||||
// Validate keys common for all the steps.
|
||||
if utils.SlicesContains(argKeys, ShellArgKey) && !utils.SlicesContains(argKeys, CommandArgKey) {
|
||||
return fmt.Errorf("workflow steps only support %q key in combination with %q key",
|
||||
ShellArgKey, CommandArgKey)
|
||||
}
|
||||
if utils.SlicesContains(argKeys, ShellArgsArgKey) && !utils.SlicesContains(argKeys, ShellArgKey) {
|
||||
return fmt.Errorf("workflow steps only support %q key in combination with %q key",
|
||||
ShellArgsArgKey, ShellArgKey)
|
||||
}
|
||||
|
||||
switch t := argMap[ShellArgsArgKey].(type) {
|
||||
case nil:
|
||||
case string:
|
||||
case []interface{}:
|
||||
for _, e := range t {
|
||||
if _, ok := e.(string); !ok {
|
||||
return fmt.Errorf("%q step %q option must contain only strings, found %v\n",
|
||||
stepName, ShellArgsArgKey, e)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%q step %q option must be a string or a list of strings, found %v\n",
|
||||
stepName, ShellArgsArgKey, t)
|
||||
}
|
||||
delete(argMap, ShellArgsArgKey)
|
||||
delete(argMap, ShellArgKey)
|
||||
|
||||
// Validate keys per step type.
|
||||
switch stepName {
|
||||
case EnvStepName:
|
||||
var argKeys []string
|
||||
for k := range args {
|
||||
argKeys = append(argKeys, k)
|
||||
}
|
||||
// Sort so tests can be deterministic.
|
||||
sort.Strings(argKeys)
|
||||
|
||||
foundNameKey := false
|
||||
for _, k := range argKeys {
|
||||
if k != NameArgKey && k != CommandArgKey && k != ValueArgKey {
|
||||
return fmt.Errorf("env steps only support keys %q, %q and %q, found key %q", NameArgKey, ValueArgKey, CommandArgKey, k)
|
||||
if k != NameArgKey && k != CommandArgKey && k != ValueArgKey && k != ShellArgKey && k != ShellArgsArgKey {
|
||||
return fmt.Errorf("env steps only support keys %q, %q, %q, %q and %q, found key %q",
|
||||
NameArgKey, ValueArgKey, CommandArgKey, ShellArgKey, ShellArgsArgKey, k)
|
||||
}
|
||||
if k == NameArgKey {
|
||||
foundNameKey = true
|
||||
}
|
||||
}
|
||||
delete(argMap, CommandArgKey)
|
||||
if !foundNameKey {
|
||||
return fmt.Errorf("env steps must have a %q key set", NameArgKey)
|
||||
}
|
||||
// If we have 3 keys at this point then they've set both command and value.
|
||||
if len(argKeys) != 2 {
|
||||
delete(argMap, NameArgKey)
|
||||
if utils.SlicesContains(argKeys, ValueArgKey) && utils.SlicesContains(argKeys, CommandArgKey) {
|
||||
return fmt.Errorf("env steps only support one of the %q or %q keys, found both",
|
||||
ValueArgKey, CommandArgKey)
|
||||
}
|
||||
delete(argMap, ValueArgKey)
|
||||
case RunStepName, MultiEnvStepName:
|
||||
argsCopy := make(map[string]string)
|
||||
for k, v := range args {
|
||||
argsCopy[k] = v
|
||||
}
|
||||
args = argsCopy
|
||||
if _, ok := args[CommandArgKey]; !ok {
|
||||
if _, ok := argMap[CommandArgKey].(string); !ok {
|
||||
return fmt.Errorf("%q step must have a %q key set", stepName, CommandArgKey)
|
||||
}
|
||||
delete(args, CommandArgKey)
|
||||
if v, ok := args[OutputArgKey]; ok {
|
||||
if stepName == RunStepName && !(v == valid.PostProcessRunOutputShow || v == valid.PostProcessRunOutputHide || v == valid.PostProcessRunOutputStripRefreshing) {
|
||||
return fmt.Errorf("run step %q option must be one of %q, %q, or %q", OutputArgKey, valid.PostProcessRunOutputShow, valid.PostProcessRunOutputHide, valid.PostProcessRunOutputStripRefreshing)
|
||||
} else if stepName == MultiEnvStepName && !(v == valid.PostProcessRunOutputShow || v == valid.PostProcessRunOutputHide) {
|
||||
return fmt.Errorf("multienv step %q option must be %q or %q", OutputArgKey, valid.PostProcessRunOutputShow, valid.PostProcessRunOutputHide)
|
||||
delete(argMap, CommandArgKey)
|
||||
if v, ok := argMap[OutputArgKey].(string); ok {
|
||||
if stepName == RunStepName && !(v == valid.PostProcessRunOutputShow ||
|
||||
v == valid.PostProcessRunOutputHide || v == valid.PostProcessRunOutputStripRefreshing) {
|
||||
return fmt.Errorf("run step %q option must be one of %q, %q, or %q",
|
||||
OutputArgKey, valid.PostProcessRunOutputShow, valid.PostProcessRunOutputHide,
|
||||
valid.PostProcessRunOutputStripRefreshing)
|
||||
} else if stepName == MultiEnvStepName && !(v == valid.PostProcessRunOutputShow ||
|
||||
v == valid.PostProcessRunOutputHide) {
|
||||
return fmt.Errorf("multienv step %q option must be %q or %q",
|
||||
OutputArgKey, valid.PostProcessRunOutputShow, valid.PostProcessRunOutputHide)
|
||||
}
|
||||
}
|
||||
delete(args, OutputArgKey)
|
||||
if len(args) > 0 {
|
||||
var argKeys []string
|
||||
for k := range args {
|
||||
argKeys = append(argKeys, k)
|
||||
}
|
||||
// Sort so tests can be deterministic.
|
||||
sort.Strings(argKeys)
|
||||
return fmt.Errorf("%q steps only support keys %q and %q, found extra keys %q", stepName, CommandArgKey, OutputArgKey, strings.Join(argKeys, ","))
|
||||
}
|
||||
delete(argMap, OutputArgKey)
|
||||
default:
|
||||
return fmt.Errorf("%q is not a valid step type", stepName)
|
||||
}
|
||||
|
||||
if len(argMap) > 0 {
|
||||
var argKeys []string
|
||||
for k := range argMap {
|
||||
argKeys = append(argKeys, k)
|
||||
}
|
||||
// Sort so tests can be deterministic.
|
||||
sort.Strings(argKeys)
|
||||
return fmt.Errorf("%q steps only support keys %q, %q, %q and %q, found extra keys %q",
|
||||
stepName, CommandArgKey, OutputArgKey, ShellArgKey, ShellArgsArgKey, strings.Join(argKeys, ","))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -278,16 +330,40 @@ func (s Step) ToValid() valid.Step {
|
||||
// After validation we assume there's only one key and it's a valid
|
||||
// step name so we just use the first one.
|
||||
for stepName, stepArgs := range s.CommandMap {
|
||||
step := valid.Step{
|
||||
StepName: stepName,
|
||||
EnvVarName: stepArgs[NameArgKey],
|
||||
RunCommand: stepArgs[CommandArgKey],
|
||||
EnvVarValue: stepArgs[ValueArgKey],
|
||||
Output: valid.PostProcessRunOutputOption(stepArgs[OutputArgKey]),
|
||||
step := valid.Step{StepName: stepName}
|
||||
if name, ok := stepArgs[NameArgKey].(string); ok {
|
||||
step.EnvVarName = name
|
||||
}
|
||||
if command, ok := stepArgs[CommandArgKey].(string); ok {
|
||||
step.RunCommand = command
|
||||
}
|
||||
if value, ok := stepArgs[ValueArgKey].(string); ok {
|
||||
step.EnvVarValue = value
|
||||
}
|
||||
if output, ok := stepArgs[OutputArgKey].(string); ok {
|
||||
step.Output = valid.PostProcessRunOutputOption(output)
|
||||
}
|
||||
if shell, ok := stepArgs[ShellArgKey].(string); ok {
|
||||
step.RunShell = &valid.CommandShell{
|
||||
Shell: shell,
|
||||
ShellArgs: []string{"-c"},
|
||||
}
|
||||
}
|
||||
if step.StepName == RunStepName && step.Output == "" {
|
||||
step.Output = valid.PostProcessRunOutputShow
|
||||
}
|
||||
|
||||
switch t := stepArgs[ShellArgsArgKey].(type) {
|
||||
case nil:
|
||||
case string:
|
||||
step.RunShell.ShellArgs = strings.Split(t, " ")
|
||||
case []interface{}:
|
||||
step.RunShell.ShellArgs = []string{}
|
||||
for _, e := range t {
|
||||
step.RunShell.ShellArgs = append(step.RunShell.ShellArgs, e.(string))
|
||||
}
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
}
|
||||
@@ -341,6 +417,17 @@ func (s *Step) unmarshalGeneric(unmarshal func(interface{}) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to unmarshal as a custom run step, ex.
|
||||
// steps:
|
||||
// - run: my command
|
||||
// We validate if the key is run later.
|
||||
var runStep map[string]string
|
||||
err = unmarshal(&runStep)
|
||||
if err == nil {
|
||||
s.StringVal = runStep
|
||||
return nil
|
||||
}
|
||||
|
||||
// This represents a step with extra_args, ex:
|
||||
// init:
|
||||
// extra_args: [a, b]
|
||||
@@ -353,26 +440,20 @@ func (s *Step) unmarshalGeneric(unmarshal func(interface{}) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This represents an env step, ex:
|
||||
// env:
|
||||
// name: k
|
||||
// value: hi //optional
|
||||
// command: exec
|
||||
var envStep map[string]map[string]string
|
||||
err = unmarshal(&envStep)
|
||||
if err == nil {
|
||||
s.CommandMap = envStep
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to unmarshal as a custom run step, ex.
|
||||
// This represents a command steps env, run, and multienv, ex:
|
||||
// steps:
|
||||
// - run: my command
|
||||
// We validate if the key is run later.
|
||||
var runStep map[string]string
|
||||
err = unmarshal(&runStep)
|
||||
// - env:
|
||||
// name: k
|
||||
// command: exec
|
||||
// - run:
|
||||
// name: test_bash_command
|
||||
// command: echo ${test_value::7}
|
||||
// shell: bash
|
||||
// shellArgs: ["--verbose", "-c"]
|
||||
var commandStep map[string]map[string]interface{}
|
||||
err = unmarshal(&commandStep)
|
||||
if err == nil {
|
||||
s.StringVal = runStep
|
||||
s.CommandMap = commandStep
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -143,12 +143,12 @@ key: value`,
|
||||
|
||||
// Errors
|
||||
{
|
||||
description: "extra args style no slice strings",
|
||||
description: "extra args style no map strings",
|
||||
input: `
|
||||
key:
|
||||
value:
|
||||
another: map`,
|
||||
expErr: "yaml: unmarshal errors:\n line 3: cannot unmarshal !!map into string",
|
||||
- value:
|
||||
another: map`,
|
||||
expErr: "yaml: unmarshal errors:\n line 3: cannot unmarshal !!seq into map[string]interface {}",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -236,6 +236,47 @@ func TestStep_Validate(t *testing.T) {
|
||||
},
|
||||
expErr: "",
|
||||
},
|
||||
{
|
||||
description: "env shell",
|
||||
input: raw.Step{
|
||||
CommandMap: EnvType{
|
||||
"env": {
|
||||
"name": "test",
|
||||
"command": "echo 123",
|
||||
"shell": "bash",
|
||||
},
|
||||
},
|
||||
},
|
||||
expErr: "",
|
||||
},
|
||||
{
|
||||
description: "env shellArgs string",
|
||||
input: raw.Step{
|
||||
CommandMap: EnvType{
|
||||
"env": {
|
||||
"name": "test",
|
||||
"command": "echo 123",
|
||||
"shell": "bash",
|
||||
"shellArgs": "-c",
|
||||
},
|
||||
},
|
||||
},
|
||||
expErr: "",
|
||||
},
|
||||
{
|
||||
description: "env shellArgs list of strings",
|
||||
input: raw.Step{
|
||||
CommandMap: EnvType{
|
||||
"env": {
|
||||
"name": "test",
|
||||
"command": "echo 123",
|
||||
"shell": "bash",
|
||||
"shellArgs": []interface{}{"-c", "--debug"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expErr: "",
|
||||
},
|
||||
{
|
||||
description: "apply extra_args",
|
||||
input: raw.Step{
|
||||
@@ -371,7 +412,7 @@ func TestStep_Validate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expErr: "env steps only support keys \"name\", \"value\" and \"command\", found key \"abc\"",
|
||||
expErr: "env steps only support keys \"name\", \"value\", \"command\", \"shell\" and \"shellArgs\", found key \"abc\"",
|
||||
},
|
||||
{
|
||||
description: "env step with both command and value set",
|
||||
@@ -386,6 +427,58 @@ func TestStep_Validate(t *testing.T) {
|
||||
},
|
||||
expErr: "env steps only support one of the \"value\" or \"command\" keys, found both",
|
||||
},
|
||||
{
|
||||
description: "env step with shell set but not command",
|
||||
input: raw.Step{
|
||||
CommandMap: EnvType{
|
||||
"env": {
|
||||
"name": "name",
|
||||
"shell": "bash",
|
||||
},
|
||||
},
|
||||
},
|
||||
expErr: "workflow steps only support \"shell\" key in combination with \"command\" key",
|
||||
},
|
||||
{
|
||||
description: "env step with shellArgs set but not shell",
|
||||
input: raw.Step{
|
||||
CommandMap: EnvType{
|
||||
"env": {
|
||||
"name": "name",
|
||||
"shellArgs": "-c",
|
||||
},
|
||||
},
|
||||
},
|
||||
expErr: "workflow steps only support \"shellArgs\" key in combination with \"shell\" key",
|
||||
},
|
||||
{
|
||||
description: "run step with shellArgs is not list of strings",
|
||||
input: raw.Step{
|
||||
CommandMap: EnvType{
|
||||
"run": {
|
||||
"name": "name",
|
||||
"command": "echo",
|
||||
"shell": "shell",
|
||||
"shellArgs": []int{42, 42},
|
||||
},
|
||||
},
|
||||
},
|
||||
expErr: "\"run\" step \"shellArgs\" option must be a string or a list of strings, found [42 42]\n",
|
||||
},
|
||||
{
|
||||
description: "run step with shellArgs contain not strings",
|
||||
input: raw.Step{
|
||||
CommandMap: EnvType{
|
||||
"run": {
|
||||
"name": "name",
|
||||
"command": "echo",
|
||||
"shell": "shell",
|
||||
"shellArgs": []interface{}{"-c", 42},
|
||||
},
|
||||
},
|
||||
},
|
||||
expErr: "\"run\" step \"shellArgs\" option must contain only strings, found 42\n",
|
||||
},
|
||||
{
|
||||
// For atlantis.yaml v2, this wouldn't parse, but now there should
|
||||
// be no error.
|
||||
@@ -611,6 +704,6 @@ func TestStep_ToValid(t *testing.T) {
|
||||
}
|
||||
|
||||
type MapType map[string]map[string][]string
|
||||
type EnvType map[string]map[string]string
|
||||
type RunType map[string]map[string]string
|
||||
type MultiEnvType map[string]map[string]string
|
||||
type EnvType map[string]map[string]interface{}
|
||||
type RunType map[string]map[string]interface{}
|
||||
type MultiEnvType map[string]map[string]interface{}
|
||||
|
||||
@@ -189,6 +189,16 @@ type Stage struct {
|
||||
Steps []Step
|
||||
}
|
||||
|
||||
// CommandShell sets up the shell for command execution
|
||||
type CommandShell struct {
|
||||
Shell string
|
||||
ShellArgs []string
|
||||
}
|
||||
|
||||
func (s CommandShell) String() string {
|
||||
return fmt.Sprintf("%s %s", s.Shell, strings.Join(s.ShellArgs, " "))
|
||||
}
|
||||
|
||||
type Step struct {
|
||||
StepName string
|
||||
ExtraArgs []string
|
||||
@@ -202,6 +212,8 @@ type Step struct {
|
||||
EnvVarName string
|
||||
// EnvVarValue is the value to set EnvVarName to.
|
||||
EnvVarValue string
|
||||
// The Shell to use for RunCommand execution.
|
||||
RunShell *CommandShell
|
||||
}
|
||||
|
||||
type Workflow struct {
|
||||
|
||||
@@ -15,13 +15,20 @@ type EnvStepRunner struct {
|
||||
// Run runs the env step command.
|
||||
// value is the value for the environment variable. If set this is returned as
|
||||
// the value. Otherwise command is run and its output is the value returned.
|
||||
func (r *EnvStepRunner) Run(ctx command.ProjectContext, command string, value string, path string, envs map[string]string) (string, error) {
|
||||
func (r *EnvStepRunner) Run(
|
||||
ctx command.ProjectContext,
|
||||
shell *valid.CommandShell,
|
||||
command string,
|
||||
value string,
|
||||
path string,
|
||||
envs map[string]string,
|
||||
) (string, error) {
|
||||
if value != "" {
|
||||
return value, nil
|
||||
}
|
||||
// Pass `false` for streamOutput because this isn't interesting to the user reading the build logs
|
||||
// in the web UI.
|
||||
res, err := r.RunStepRunner.Run(ctx, command, path, envs, false, valid.PostProcessRunOutputShow)
|
||||
res, err := r.RunStepRunner.Run(ctx, shell, command, path, envs, false, valid.PostProcessRunOutputShow)
|
||||
// Trim newline from res to support running `echo env_value` which has
|
||||
// a newline. We don't recommend users run echo -n env_value to remove the
|
||||
// newline because -n doesn't work in the sh shell which is what we use
|
||||
|
||||
@@ -77,7 +77,7 @@ func TestEnvStepRunner_Run(t *testing.T) {
|
||||
TerraformVersion: tfVersion,
|
||||
ProjectName: c.ProjectName,
|
||||
}
|
||||
value, err := envRunner.Run(ctx, c.Command, c.Value, tmpDir, map[string]string(nil))
|
||||
value, err := envRunner.Run(ctx, nil, c.Command, c.Value, tmpDir, map[string]string(nil))
|
||||
if c.ExpErr != "" {
|
||||
ErrContains(t, c.ExpErr, err)
|
||||
return
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/runatlantis/atlantis/server/core/config/valid"
|
||||
"github.com/runatlantis/atlantis/server/events/command"
|
||||
"github.com/runatlantis/atlantis/server/events/terraform/ansi"
|
||||
"github.com/runatlantis/atlantis/server/jobs"
|
||||
@@ -33,10 +34,27 @@ type ShellCommandRunner struct {
|
||||
outputHandler jobs.ProjectCommandOutputHandler
|
||||
streamOutput bool
|
||||
cmd *exec.Cmd
|
||||
shell *valid.CommandShell
|
||||
}
|
||||
|
||||
func NewShellCommandRunner(command string, environ []string, workingDir string, streamOutput bool, outputHandler jobs.ProjectCommandOutputHandler) *ShellCommandRunner {
|
||||
cmd := exec.Command("sh", "-c", command) // #nosec
|
||||
func NewShellCommandRunner(
|
||||
shell *valid.CommandShell,
|
||||
command string,
|
||||
environ []string,
|
||||
workingDir string,
|
||||
streamOutput bool,
|
||||
outputHandler jobs.ProjectCommandOutputHandler,
|
||||
) *ShellCommandRunner {
|
||||
if shell == nil {
|
||||
shell = &valid.CommandShell{
|
||||
Shell: "sh",
|
||||
ShellArgs: []string{"-c"},
|
||||
}
|
||||
}
|
||||
var args []string
|
||||
args = append(args, shell.ShellArgs...)
|
||||
args = append(args, command)
|
||||
cmd := exec.Command(shell.Shell, args...) // #nosec
|
||||
cmd.Env = environ
|
||||
cmd.Dir = workingDir
|
||||
|
||||
@@ -46,6 +64,7 @@ func NewShellCommandRunner(command string, environ []string, workingDir string,
|
||||
outputHandler: outputHandler,
|
||||
streamOutput: streamOutput,
|
||||
cmd: cmd,
|
||||
shell: shell,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +111,10 @@ func (s *ShellCommandRunner) RunCommandAsync(ctx command.ProjectContext) (chan<-
|
||||
stderr, _ := s.cmd.StderrPipe()
|
||||
stdin, _ := s.cmd.StdinPipe()
|
||||
|
||||
ctx.Log.Debug("starting %q in %q", s.command, s.workingDir)
|
||||
ctx.Log.Debug("starting '%s %q' in '%s'", s.shell.String(), s.command, s.workingDir)
|
||||
err := s.cmd.Start()
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "running %q in %q", s.command, s.workingDir)
|
||||
err = errors.Wrapf(err, "running '%s %q' in '%s'", s.shell.String(), s.command, s.workingDir)
|
||||
ctx.Log.Err(err.Error())
|
||||
outCh <- Line{Err: err}
|
||||
return
|
||||
@@ -154,11 +173,13 @@ func (s *ShellCommandRunner) RunCommandAsync(ctx command.ProjectContext) (chan<-
|
||||
|
||||
// We're done now. Send an error if there was one.
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "running %q in %q", s.command, s.workingDir)
|
||||
err = errors.Wrapf(err, "running '%s %q' in '%s'",
|
||||
s.shell.String(), s.command, s.workingDir)
|
||||
log.Err(err.Error())
|
||||
outCh <- Line{Err: err}
|
||||
} else {
|
||||
log.Info("successfully ran %q in %q", s.command, s.workingDir)
|
||||
log.Info("successfully ran '%s %q' in '%s'",
|
||||
s.shell.String(), s.command, s.workingDir)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestShellCommandRunner_Run(t *testing.T) {
|
||||
expectedOutput := fmt.Sprintf("%s\n", strings.Join(c.ExpLines, "\n"))
|
||||
|
||||
// Run once with streaming enabled
|
||||
runner := models.NewShellCommandRunner(c.Command, environ, cwd, true, projectCmdOutputHandler)
|
||||
runner := models.NewShellCommandRunner(nil, c.Command, environ, cwd, true, projectCmdOutputHandler)
|
||||
output, err := runner.Run(ctx)
|
||||
Ok(t, err)
|
||||
Equals(t, expectedOutput, output)
|
||||
@@ -68,7 +68,7 @@ func TestShellCommandRunner_Run(t *testing.T) {
|
||||
// command output handler should not have received anything
|
||||
|
||||
projectCmdOutputHandler = mocks.NewMockProjectCommandOutputHandler()
|
||||
runner = models.NewShellCommandRunner(c.Command, environ, cwd, false, projectCmdOutputHandler)
|
||||
runner = models.NewShellCommandRunner(nil, c.Command, environ, cwd, false, projectCmdOutputHandler)
|
||||
output, err = runner.Run(ctx)
|
||||
Ok(t, err)
|
||||
Equals(t, expectedOutput, output)
|
||||
|
||||
@@ -16,8 +16,15 @@ type MultiEnvStepRunner struct {
|
||||
|
||||
// Run runs the multienv step command.
|
||||
// The command must return a json string containing the array of name-value pairs that are being added as extra environment variables
|
||||
func (r *MultiEnvStepRunner) Run(ctx command.ProjectContext, command string, path string, envs map[string]string, postProcessOutput valid.PostProcessRunOutputOption) (string, error) {
|
||||
res, err := r.RunStepRunner.Run(ctx, command, path, envs, false, postProcessOutput)
|
||||
func (r *MultiEnvStepRunner) Run(
|
||||
ctx command.ProjectContext,
|
||||
shell *valid.CommandShell,
|
||||
command string,
|
||||
path string,
|
||||
envs map[string]string,
|
||||
postProcessOutput valid.PostProcessRunOutputOption,
|
||||
) (string, error) {
|
||||
res, err := r.RunStepRunner.Run(ctx, shell, command, path, envs, false, postProcessOutput)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func TestMultiEnvStepRunner_Run(t *testing.T) {
|
||||
ProjectName: c.ProjectName,
|
||||
}
|
||||
envMap := make(map[string]string)
|
||||
value, err := multiEnvStepRunner.Run(ctx, c.Command, tmpDir, envMap, valid.PostProcessRunOutputShow)
|
||||
value, err := multiEnvStepRunner.Run(ctx, nil, c.Command, tmpDir, envMap, valid.PostProcessRunOutputShow)
|
||||
if c.ExpErr != "" {
|
||||
ErrContains(t, c.ExpErr, err)
|
||||
return
|
||||
|
||||
@@ -22,7 +22,15 @@ type RunStepRunner struct {
|
||||
ProjectCmdOutputHandler jobs.ProjectCommandOutputHandler
|
||||
}
|
||||
|
||||
func (r *RunStepRunner) Run(ctx command.ProjectContext, command string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) (string, error) {
|
||||
func (r *RunStepRunner) Run(
|
||||
ctx command.ProjectContext,
|
||||
shell *valid.CommandShell,
|
||||
command string,
|
||||
path string,
|
||||
envs map[string]string,
|
||||
streamOutput bool,
|
||||
postProcessOutput valid.PostProcessRunOutputOption,
|
||||
) (string, error) {
|
||||
tfVersion := r.DefaultTFVersion
|
||||
if ctx.TerraformVersion != nil {
|
||||
tfVersion = ctx.TerraformVersion
|
||||
@@ -68,7 +76,7 @@ func (r *RunStepRunner) Run(ctx command.ProjectContext, command string, path str
|
||||
finalEnvVars = append(finalEnvVars, fmt.Sprintf("%s=%s", key, val))
|
||||
}
|
||||
|
||||
runner := models.NewShellCommandRunner(command, finalEnvVars, path, streamOutput, r.ProjectCmdOutputHandler)
|
||||
runner := models.NewShellCommandRunner(shell, command, finalEnvVars, path, streamOutput, r.ProjectCmdOutputHandler)
|
||||
output, err := runner.Run(ctx)
|
||||
|
||||
if postProcessOutput == valid.PostProcessRunOutputStripRefreshing {
|
||||
|
||||
@@ -145,7 +145,7 @@ func TestRunStepRunner_Run(t *testing.T) {
|
||||
ProjectName: c.ProjectName,
|
||||
EscapedCommentArgs: []string{"-target=resource1", "-target=resource2"},
|
||||
}
|
||||
out, err := r.Run(ctx, c.Command, tmpDir, map[string]string{"test": "var"}, true, valid.PostProcessRunOutputShow)
|
||||
out, err := r.Run(ctx, nil, c.Command, tmpDir, map[string]string{"test": "var"}, true, valid.PostProcessRunOutputShow)
|
||||
if c.ExpErr != "" {
|
||||
ErrContains(t, c.ExpErr, err)
|
||||
return
|
||||
|
||||
@@ -466,7 +466,7 @@ func (c *DefaultClient) RunCommandAsync(ctx command.ProjectContext, path string,
|
||||
envVars = append(envVars, fmt.Sprintf("%s=%s", key, val))
|
||||
}
|
||||
|
||||
runner := models.NewShellCommandRunner(cmd, envVars, path, true, c.projectCmdOutputHandler)
|
||||
runner := models.NewShellCommandRunner(nil, cmd, envVars, path, true, c.projectCmdOutputHandler)
|
||||
inCh, outCh := runner.RunCommandAsync(ctx)
|
||||
return inCh, outCh
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ func TestDefaultClient_RunCommandAsync_ExitOne(t *testing.T) {
|
||||
_, outCh := client.RunCommandAsync(ctx, tmp, []string{"dying", "&&", "exit", "1"}, map[string]string{}, nil, "workspace")
|
||||
|
||||
out, err := waitCh(outCh)
|
||||
ErrEquals(t, fmt.Sprintf(`running "echo dying && exit 1" in %q: exit status 1`, tmp), err)
|
||||
ErrEquals(t, fmt.Sprintf(`running 'sh -c "echo dying && exit 1"' in '%s': exit status 1`, tmp), err)
|
||||
// Test that we still get our output.
|
||||
Equals(t, "dying", out)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func NewMockCustomStepRunner(options ...pegomock.Option) *MockCustomStepRunner {
|
||||
func (mock *MockCustomStepRunner) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }
|
||||
func (mock *MockCustomStepRunner) FailHandler() pegomock.FailHandler { return mock.fail }
|
||||
|
||||
func (mock *MockCustomStepRunner) Run(ctx command.ProjectContext, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) (string, error) {
|
||||
func (mock *MockCustomStepRunner) Run(ctx command.ProjectContext, shell *valid.CommandShell, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) (string, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockCustomStepRunner().")
|
||||
}
|
||||
@@ -82,7 +82,7 @@ type VerifierMockCustomStepRunner struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockCustomStepRunner) Run(ctx command.ProjectContext, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) *MockCustomStepRunner_Run_OngoingVerification {
|
||||
func (verifier *VerifierMockCustomStepRunner) Run(ctx command.ProjectContext, shell *valid.CommandShell, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) *MockCustomStepRunner_Run_OngoingVerification {
|
||||
params := []pegomock.Param{ctx, cmd, path, envs, streamOutput, postProcessOutput}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Run", params, verifier.timeout)
|
||||
return &MockCustomStepRunner_Run_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
pegomock "github.com/petergtz/pegomock/v4"
|
||||
command "github.com/runatlantis/atlantis/server/events/command"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
pegomock "github.com/petergtz/pegomock/v4"
|
||||
"github.com/runatlantis/atlantis/server/core/config/valid"
|
||||
command "github.com/runatlantis/atlantis/server/events/command"
|
||||
)
|
||||
|
||||
type MockEnvStepRunner struct {
|
||||
@@ -25,7 +27,7 @@ func NewMockEnvStepRunner(options ...pegomock.Option) *MockEnvStepRunner {
|
||||
func (mock *MockEnvStepRunner) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }
|
||||
func (mock *MockEnvStepRunner) FailHandler() pegomock.FailHandler { return mock.fail }
|
||||
|
||||
func (mock *MockEnvStepRunner) Run(ctx command.ProjectContext, cmd string, value string, path string, envs map[string]string) (string, error) {
|
||||
func (mock *MockEnvStepRunner) Run(ctx command.ProjectContext, shell *valid.CommandShell, cmd string, value string, path string, envs map[string]string) (string, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockEnvStepRunner().")
|
||||
}
|
||||
@@ -81,7 +83,7 @@ type VerifierMockEnvStepRunner struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockEnvStepRunner) Run(ctx command.ProjectContext, cmd string, value string, path string, envs map[string]string) *MockEnvStepRunner_Run_OngoingVerification {
|
||||
func (verifier *VerifierMockEnvStepRunner) Run(ctx command.ProjectContext, shell *valid.CommandShell, cmd string, value string, path string, envs map[string]string) *MockEnvStepRunner_Run_OngoingVerification {
|
||||
params := []pegomock.Param{ctx, cmd, value, path, envs}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Run", params, verifier.timeout)
|
||||
return &MockEnvStepRunner_Run_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
|
||||
@@ -65,20 +65,42 @@ type StepRunner interface {
|
||||
// CustomStepRunner runs custom run steps.
|
||||
type CustomStepRunner interface {
|
||||
// Run cmd in path.
|
||||
Run(ctx command.ProjectContext, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) (string, error)
|
||||
Run(
|
||||
ctx command.ProjectContext,
|
||||
shell *valid.CommandShell,
|
||||
cmd string,
|
||||
path string,
|
||||
envs map[string]string,
|
||||
streamOutput bool,
|
||||
postProcessOutput valid.PostProcessRunOutputOption,
|
||||
) (string, error)
|
||||
}
|
||||
|
||||
//go:generate pegomock generate --package mocks -o mocks/mock_env_step_runner.go EnvStepRunner
|
||||
|
||||
// EnvStepRunner runs env steps.
|
||||
type EnvStepRunner interface {
|
||||
Run(ctx command.ProjectContext, cmd string, value string, path string, envs map[string]string) (string, error)
|
||||
Run(
|
||||
ctx command.ProjectContext,
|
||||
shell *valid.CommandShell,
|
||||
cmd string,
|
||||
value string,
|
||||
path string,
|
||||
envs map[string]string,
|
||||
) (string, error)
|
||||
}
|
||||
|
||||
// MultiEnvStepRunner runs multienv steps.
|
||||
type MultiEnvStepRunner interface {
|
||||
// Run cmd in path.
|
||||
Run(ctx command.ProjectContext, cmd string, path string, envs map[string]string, postProcessOutput valid.PostProcessRunOutputOption) (string, error)
|
||||
Run(
|
||||
ctx command.ProjectContext,
|
||||
shell *valid.CommandShell,
|
||||
cmd string,
|
||||
path string,
|
||||
envs map[string]string,
|
||||
postProcessOutput valid.PostProcessRunOutputOption,
|
||||
) (string, error)
|
||||
}
|
||||
|
||||
//go:generate pegomock generate --package mocks -o mocks/mock_webhooks_sender.go WebhooksSender
|
||||
@@ -790,15 +812,15 @@ func (p *DefaultProjectCommandRunner) runSteps(steps []valid.Step, ctx command.P
|
||||
case "state_rm":
|
||||
out, err = p.StateRmStepRunner.Run(ctx, step.ExtraArgs, absPath, envs)
|
||||
case "run":
|
||||
out, err = p.RunStepRunner.Run(ctx, step.RunCommand, absPath, envs, true, step.Output)
|
||||
out, err = p.RunStepRunner.Run(ctx, step.RunShell, step.RunCommand, absPath, envs, true, step.Output)
|
||||
case "env":
|
||||
out, err = p.EnvStepRunner.Run(ctx, step.RunCommand, step.EnvVarValue, absPath, envs)
|
||||
out, err = p.EnvStepRunner.Run(ctx, step.RunShell, step.RunCommand, step.EnvVarValue, absPath, envs)
|
||||
envs[step.EnvVarName] = out
|
||||
// We reset out to the empty string because we don't want it to
|
||||
// be printed to the PR, it's solely to set the environment variable.
|
||||
out = ""
|
||||
case "multienv":
|
||||
out, err = p.MultiEnvStepRunner.Run(ctx, step.RunCommand, absPath, envs, step.Output)
|
||||
out, err = p.MultiEnvStepRunner.Run(ctx, step.RunShell, step.RunCommand, absPath, envs, step.Output)
|
||||
}
|
||||
|
||||
if out != "" {
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestDefaultProjectCommandRunner_Plan(t *testing.T) {
|
||||
When(mockInit.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("init", nil)
|
||||
When(mockPlan.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("plan", nil)
|
||||
When(mockApply.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("apply", nil)
|
||||
When(mockRun.Run(ctx, "", repoDir, expEnvs, true, "")).ThenReturn("run", nil)
|
||||
When(mockRun.Run(ctx, nil, "", repoDir, expEnvs, true, "")).ThenReturn("run", nil)
|
||||
res := runner.Plan(ctx)
|
||||
|
||||
Assert(t, res.PlanSuccess != nil, "exp plan success")
|
||||
@@ -115,7 +115,7 @@ func TestDefaultProjectCommandRunner_Plan(t *testing.T) {
|
||||
case "apply":
|
||||
mockApply.VerifyWasCalledOnce().Run(ctx, nil, repoDir, expEnvs)
|
||||
case "run":
|
||||
mockRun.VerifyWasCalledOnce().Run(ctx, "", repoDir, expEnvs, true, "")
|
||||
mockRun.VerifyWasCalledOnce().Run(ctx, nil, "", repoDir, expEnvs, true, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -455,8 +455,8 @@ func TestDefaultProjectCommandRunner_Apply(t *testing.T) {
|
||||
When(mockInit.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("init", nil)
|
||||
When(mockPlan.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("plan", nil)
|
||||
When(mockApply.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("apply", nil)
|
||||
When(mockRun.Run(ctx, "", repoDir, expEnvs, true, "")).ThenReturn("run", nil)
|
||||
When(mockEnv.Run(ctx, "", "value", repoDir, make(map[string]string))).ThenReturn("value", nil)
|
||||
When(mockRun.Run(ctx, nil, "", repoDir, expEnvs, true, "")).ThenReturn("run", nil)
|
||||
When(mockEnv.Run(ctx, nil, "", "value", repoDir, make(map[string]string))).ThenReturn("value", nil)
|
||||
|
||||
res := runner.Apply(ctx)
|
||||
Equals(t, c.expOut, res.ApplySuccess)
|
||||
@@ -471,9 +471,9 @@ func TestDefaultProjectCommandRunner_Apply(t *testing.T) {
|
||||
case "apply":
|
||||
mockApply.VerifyWasCalledOnce().Run(ctx, nil, repoDir, expEnvs)
|
||||
case "run":
|
||||
mockRun.VerifyWasCalledOnce().Run(ctx, "", repoDir, expEnvs, true, "")
|
||||
mockRun.VerifyWasCalledOnce().Run(ctx, nil, "", repoDir, expEnvs, true, "")
|
||||
case "env":
|
||||
mockEnv.VerifyWasCalledOnce().Run(ctx, "", "value", repoDir, expEnvs)
|
||||
mockEnv.VerifyWasCalledOnce().Run(ctx, nil, "", "value", repoDir, expEnvs)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user