feat: Add Support for Customising the Shell for Pre and Post Workflow Hooks (#3451)

This commit is contained in:
Simon Heather
2023-07-07 23:34:01 +01:00
committed by GitHub
parent 76c482b83a
commit b8994190be
15 changed files with 523 additions and 70 deletions

View File

@@ -6,14 +6,12 @@ workflows](custom-workflows.html#custom-run-command) in that they are run
outside of Atlantis commands. Which means they do not surface their output
back to the PR as a comment.
Post workflow hooks also only allow `run` and `description` commands.
[[toc]]
## Usage
Post workflow hooks can only be specified in the Server-Side Repo Config under
`repos` key.
the `repos` key.
## Use Cases
@@ -45,6 +43,25 @@ repos:
# ...
```
## Customizing the Shell
By default, the commands will be run using the 'sh' shell with an argument of '-c'. This
can be customized using the `shell` and `shellArgs` keys.
Example:
```yaml
repos:
- id: /.*/
post_workflow_hooks:
- run: |
echo 'atlantis.yaml config:'
cat atlantis.yaml
description: atlantis.yaml report
shell: bash
shellArgs: -cv
```
## Reference
### Custom `run` Command
@@ -60,6 +77,8 @@ command](custom-workflows.html#custom-run-command).
| ----------- | ------ | ------- | -------- | --------------------- |
| run | string | none | no | Run a custom command |
| description | string | none | no | Post hook description |
| shell | string | 'sh' | no | The shell to use for running the command |
| shellArgs | string | '-c' | no | The shell arguments to use for running the command |
::: tip Notes
* `run` commands are executed with the following environment variables:

View File

@@ -4,27 +4,29 @@ Pre workflow hooks can be defined to run scripts before default or custom
workflows are executed. Pre workflow hooks differ from [custom
workflows](custom-workflows.html#custom-run-command) in several ways.
1. Pre workflow hooks do not require for repository configuration to be
1. Pre workflow hooks do not require the repository configuration to be
present. This can be utilized to [dynamically generate repo configs](pre-workflow-hooks.html#dynamic-repo-config-generation).
2. Pre workflow hooks are run outside of Atlantis commands. Which means
they do not surface their output back to the PR as a comment.
3. Pre workflow hooks only allow `run` and `description` commands.
[[toc]]
## Usage
Pre workflow hooks can only be specified in the Server-Side Repo Config under
`repos` key.
Pre workflow hooks can only be specified in the Server-Side Repo Config under the
`repos` key.
::: tip Note
`pre-workflow-hooks` do not prevent Atlantis from executing its
workflows(`plan`, `apply`) even if a `run` command exits with an error.
:::
## Use Cases
### Dynamic Repo Config Generation
If you want generate your `atlantis.yaml` before Atlantis can parse it. You
can add a `run` command to `pre_workflow_hooks`. Your Repo config will be generated
right before Atlantis can parse it.
To generate the repo `atlantis.yaml` before Atlantis can parse it,
add a `run` command to `pre_workflow_hooks`. Your Repo config will be generated
right before Atlantis parses it.
```yaml
repos:
@@ -33,17 +35,43 @@ repos:
- run: ./repo-config-generator.sh
description: Generating configs
```
## Customizing the Shell
By default, the command will be run using the 'sh' shell with an argument of '-c'. This
can be customized using the `shell` and `shellArgs` keys.
Example:
```yaml
repos:
- id: /.*/
pre_workflow_hooks:
- run: |
echo "generating atlantis.yaml"
terragrunt-atlantis-config generate --output atlantis.yaml --autoplan --parallel
description: Generating atlantis.yaml
shell: bash
shellArgs: -cv
```
## Reference
### Custom `run` Command
This is very similar to [custom workflow run
command](custom-workflows.html#custom-run-command).
This is very similar to the [custom workflow run
command](custom-workflows.html#custom-run-command).
```yaml
- run: custom-command
```
| Key | Type | Default | Required | Description |
| ----------- | ------ | ------- | -------- | -------------------- |
| run | string | none | no | Run a custom command |
| description | string | none | no | Pre hook description |
| shell | string | 'sh' | no | The shell to use for running the command |
| shellArgs | string | '-c' | no | The shell arguments to use for running the command |
::: tip Notes
* `run` commands are executed with the following environment variables:
@@ -64,4 +92,3 @@ command](custom-workflows.html#custom-run-command).
* `COMMAND_NAME` - The name of the command that is being executed, i.e. `plan`, `apply` etc.
* `OUTPUT_STATUS_FILE` - An output file to customize the success or failure status. ex. `echo 'failure' > $OUTPUT_STATUS_FILE`.
:::

View File

@@ -610,9 +610,11 @@ func TestGitHubWorkflow(t *testing.T) {
expNumHooks := len(c.Comments) + 1 - c.ExpParseFailedCount
// Let's verify the pre-workflow hook was called for each comment including the pull request opened event
mockPreWorkflowHookRunner.VerifyWasCalled(Times(expNumHooks)).Run(Any[models.WorkflowHookCommandContext](), Eq("some dummy command"), Any[string]())
mockPreWorkflowHookRunner.VerifyWasCalled(Times(expNumHooks)).Run(Any[models.WorkflowHookCommandContext](),
Eq("some dummy command"), Any[string](), Any[string](), Any[string]())
// Let's verify the post-workflow hook was called for each comment including the pull request opened event
mockPostWorkflowHookRunner.VerifyWasCalled(Times(expNumHooks)).Run(Any[models.WorkflowHookCommandContext](), Eq("some post dummy command"), Any[string]())
mockPostWorkflowHookRunner.VerifyWasCalled(Times(expNumHooks)).Run(Any[models.WorkflowHookCommandContext](),
Eq("some post dummy command"), Any[string](), Any[string](), Any[string]())
// Now we're ready to verify Atlantis made all the comments back (or
// replies) that we expect. We expect each plan to have 1 comment,
@@ -794,7 +796,8 @@ func TestSimpleWorkflow_terraformLockFile(t *testing.T) {
}
// Let's verify the pre-workflow hook was called for each comment including the pull request opened event
mockPreWorkflowHookRunner.VerifyWasCalled(Times(2)).Run(Any[models.WorkflowHookCommandContext](), Eq("some dummy command"), Any[string]())
mockPreWorkflowHookRunner.VerifyWasCalled(Times(2)).Run(Any[models.WorkflowHookCommandContext](),
Eq("some dummy command"), Any[string](), Any[string](), Any[string]())
// Now we're ready to verify Atlantis made all the comments back (or
// replies) that we expect. We expect each plan to have 1 comment,

View File

@@ -76,6 +76,8 @@ func (s WorkflowHook) ToValid() *valid.WorkflowHook {
StepName: RunStepName,
RunCommand: s.StringVal["run"],
StepDescription: s.StringVal["description"],
Shell: s.StringVal["shell"],
ShellArgs: s.StringVal["shellArgs"],
}
}

View File

@@ -109,6 +109,8 @@ type WorkflowHook struct {
StepName string
RunCommand string
StepDescription string
Shell string
ShellArgs string
}
// DefaultApplyStage is the Atlantis default apply stage.

View File

@@ -25,11 +25,11 @@ func NewMockPostWorkflowHookRunner(options ...pegomock.Option) *MockPostWorkflow
func (mock *MockPostWorkflowHookRunner) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }
func (mock *MockPostWorkflowHookRunner) FailHandler() pegomock.FailHandler { return mock.fail }
func (mock *MockPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, string, error) {
func (mock *MockPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, shell string, shellArgs string, path string) (string, string, error) {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockPostWorkflowHookRunner().")
}
params := []pegomock.Param{ctx, command, path}
params := []pegomock.Param{ctx, command, shell, shellArgs, path}
result := pegomock.GetGenericMockFrom(mock).Invoke("Run", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 string
var ret1 string
@@ -85,8 +85,8 @@ type VerifierMockPostWorkflowHookRunner struct {
timeout time.Duration
}
func (verifier *VerifierMockPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, path string) *MockPostWorkflowHookRunner_Run_OngoingVerification {
params := []pegomock.Param{ctx, command, path}
func (verifier *VerifierMockPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, shell string, shellArgs string, path string) *MockPostWorkflowHookRunner_Run_OngoingVerification {
params := []pegomock.Param{ctx, command, shell, shellArgs, path}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Run", params, verifier.timeout)
return &MockPostWorkflowHookRunner_Run_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
@@ -96,12 +96,12 @@ type MockPostWorkflowHookRunner_Run_OngoingVerification struct {
methodInvocations []pegomock.MethodInvocation
}
func (c *MockPostWorkflowHookRunner_Run_OngoingVerification) GetCapturedArguments() (models.WorkflowHookCommandContext, string, string) {
ctx, command, path := c.GetAllCapturedArguments()
return ctx[len(ctx)-1], command[len(command)-1], path[len(path)-1]
func (c *MockPostWorkflowHookRunner_Run_OngoingVerification) GetCapturedArguments() (models.WorkflowHookCommandContext, string, string, string, string) {
ctx, command, shell, shellArgs, path := c.GetAllCapturedArguments()
return ctx[len(ctx)-1], command[len(command)-1], shell[len(shell)-1], shellArgs[len(shellArgs)-1], path[len(path)-1]
}
func (c *MockPostWorkflowHookRunner_Run_OngoingVerification) GetAllCapturedArguments() (_param0 []models.WorkflowHookCommandContext, _param1 []string, _param2 []string) {
func (c *MockPostWorkflowHookRunner_Run_OngoingVerification) GetAllCapturedArguments() (_param0 []models.WorkflowHookCommandContext, _param1 []string, _param2 []string, _param3 []string, _param4 []string) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.WorkflowHookCommandContext, len(c.methodInvocations))
@@ -116,6 +116,14 @@ func (c *MockPostWorkflowHookRunner_Run_OngoingVerification) GetAllCapturedArgum
for u, param := range params[2] {
_param2[u] = param.(string)
}
_param3 = make([]string, len(c.methodInvocations))
for u, param := range params[3] {
_param3[u] = param.(string)
}
_param4 = make([]string, len(c.methodInvocations))
for u, param := range params[4] {
_param4[u] = param.(string)
}
}
return
}

View File

@@ -25,11 +25,11 @@ func NewMockPreWorkflowHookRunner(options ...pegomock.Option) *MockPreWorkflowHo
func (mock *MockPreWorkflowHookRunner) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }
func (mock *MockPreWorkflowHookRunner) FailHandler() pegomock.FailHandler { return mock.fail }
func (mock *MockPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, string, error) {
func (mock *MockPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, shell string, shellArgs string, path string) (string, string, error) {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockPreWorkflowHookRunner().")
}
params := []pegomock.Param{ctx, command, path}
params := []pegomock.Param{ctx, command, shell, shellArgs, path}
result := pegomock.GetGenericMockFrom(mock).Invoke("Run", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
var ret0 string
var ret1 string
@@ -85,8 +85,8 @@ type VerifierMockPreWorkflowHookRunner struct {
timeout time.Duration
}
func (verifier *VerifierMockPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, path string) *MockPreWorkflowHookRunner_Run_OngoingVerification {
params := []pegomock.Param{ctx, command, path}
func (verifier *VerifierMockPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, shell string, shellArgs string, path string) *MockPreWorkflowHookRunner_Run_OngoingVerification {
params := []pegomock.Param{ctx, command, shell, shellArgs, path}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Run", params, verifier.timeout)
return &MockPreWorkflowHookRunner_Run_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
@@ -96,12 +96,12 @@ type MockPreWorkflowHookRunner_Run_OngoingVerification struct {
methodInvocations []pegomock.MethodInvocation
}
func (c *MockPreWorkflowHookRunner_Run_OngoingVerification) GetCapturedArguments() (models.WorkflowHookCommandContext, string, string) {
ctx, command, path := c.GetAllCapturedArguments()
return ctx[len(ctx)-1], command[len(command)-1], path[len(path)-1]
func (c *MockPreWorkflowHookRunner_Run_OngoingVerification) GetCapturedArguments() (models.WorkflowHookCommandContext, string, string, string, string) {
ctx, command, shell, shellArgs, path := c.GetAllCapturedArguments()
return ctx[len(ctx)-1], command[len(command)-1], shell[len(shell)-1], shellArgs[len(shellArgs)-1], path[len(path)-1]
}
func (c *MockPreWorkflowHookRunner_Run_OngoingVerification) GetAllCapturedArguments() (_param0 []models.WorkflowHookCommandContext, _param1 []string, _param2 []string) {
func (c *MockPreWorkflowHookRunner_Run_OngoingVerification) GetAllCapturedArguments() (_param0 []models.WorkflowHookCommandContext, _param1 []string, _param2 []string, _param3 []string, _param4 []string) {
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
if len(params) > 0 {
_param0 = make([]models.WorkflowHookCommandContext, len(c.methodInvocations))
@@ -116,6 +116,14 @@ func (c *MockPreWorkflowHookRunner_Run_OngoingVerification) GetAllCapturedArgume
for u, param := range params[2] {
_param2[u] = param.(string)
}
_param3 = make([]string, len(c.methodInvocations))
for u, param := range params[3] {
_param3[u] = param.(string)
}
_param4 = make([]string, len(c.methodInvocations))
for u, param := range params[4] {
_param4[u] = param.(string)
}
}
return
}

View File

@@ -13,17 +13,18 @@ import (
//go:generate pegomock generate --package mocks -o mocks/mock_post_workflows_hook_runner.go PostWorkflowHookRunner
type PostWorkflowHookRunner interface {
Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, string, error)
Run(ctx models.WorkflowHookCommandContext, command string, shell string, shellArgs string, path string) (string, string, error)
}
type DefaultPostWorkflowHookRunner struct {
OutputHandler jobs.ProjectCommandOutputHandler
}
func (wh DefaultPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, string, error) {
func (wh DefaultPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, shell string, shellArgs string, path string) (string, string, error) {
outputFilePath := filepath.Join(path, "OUTPUT_STATUS_FILE")
cmd := exec.Command("sh", "-c", command) // #nosec
shellArgsSlice := append(strings.Split(shellArgs, " "), command)
cmd := exec.Command(shell, shellArgsSlice...) // #nosec
cmd.Dir = path
baseEnvVars := os.Environ()
@@ -58,7 +59,7 @@ func (wh DefaultPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContex
wh.OutputHandler.SendWorkflowHook(ctx, "\n", true)
if err != nil {
err = fmt.Errorf("%s: running %q in %q: \n%s", err, command, path, out)
err = fmt.Errorf("%s: running %q in %q: \n%s", err, shell+" "+shellArgs+" "+command, path, out)
ctx.Log.Debug("error: %s", err)
return string(out), "", err
}
@@ -70,12 +71,12 @@ func (wh DefaultPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContex
var customStatusErr error
customStatusOut, customStatusErr = os.ReadFile(outputFilePath)
if customStatusErr != nil {
err = fmt.Errorf("%s: running %q in %q: \n%s", err, command, path, out)
err = fmt.Errorf("%s: running %q in %q: \n%s", err, shell+" "+shellArgs+" "+command, path, out)
ctx.Log.Debug("error: %s", err)
return string(out), "", err
}
}
ctx.Log.Info("successfully ran %q in %q", command, path)
return outString, strings.Trim(string(customStatusOut), "\n"), nil
ctx.Log.Info("successfully ran %q in %q", shell+" "+shellArgs+" "+command, path)
return string(out), strings.Trim(string(customStatusOut), "\n"), nil
}

View File

@@ -15,72 +15,122 @@ import (
)
func TestPostWorkflowHookRunner_Run(t *testing.T) {
defaultShell := "sh"
defaultShellArgs := "-c"
cases := []struct {
Command string
Shell string
ShellArgs string
ExpOut string
ExpErr string
ExpDescription string
}{
{
Command: "",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo hi",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "hi\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: `printf \'your main.tf file does not provide default region.\\ncheck\'`,
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: `'your`,
ExpErr: "",
ExpDescription: "",
},
{
Command: `printf 'your main.tf file does not provide default region.\ncheck'`,
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "your main.tf file does not provide default region.\r\ncheck",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo 'a",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "sh: 1: Syntax error: Unterminated quoted string\r\n",
ExpErr: "exit status 2: running \"echo 'a\" in",
ExpErr: "exit status 2: running \"sh -c echo 'a\" in",
ExpDescription: "",
},
{
Command: "echo hi >> file && cat file",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "hi\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "lkjlkj",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "sh: 1: lkjlkj: not found\r\n",
ExpErr: "exit status 127: running \"lkjlkj\" in",
ExpErr: "exit status 127: running \"sh -c lkjlkj\" in",
ExpDescription: "",
},
{
Command: "echo base_repo_name=$BASE_REPO_NAME base_repo_owner=$BASE_REPO_OWNER head_repo_name=$HEAD_REPO_NAME head_repo_owner=$HEAD_REPO_OWNER head_branch_name=$HEAD_BRANCH_NAME head_commit=$HEAD_COMMIT base_branch_name=$BASE_BRANCH_NAME pull_num=$PULL_NUM pull_url=$PULL_URL pull_author=$PULL_AUTHOR",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "base_repo_name=basename base_repo_owner=baseowner head_repo_name=headname head_repo_owner=headowner head_branch_name=add-feat head_commit=12345abcdef base_branch_name=main pull_num=2 pull_url=https://github.com/runatlantis/atlantis/pull/2 pull_author=acme\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo user_name=$USER_NAME",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "user_name=acme-user\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo something > $OUTPUT_STATUS_FILE",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "",
ExpErr: "",
ExpDescription: "something",
},
{
Command: "echo shell test 1",
Shell: "bash",
ShellArgs: defaultShellArgs,
ExpOut: "shell test 1\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo shell test 2",
Shell: defaultShell,
ShellArgs: "-cx",
ExpOut: "+ echo shell test 2\r\nshell test 2\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo shell test 3",
Shell: "bash",
ShellArgs: "-cv",
ExpOut: "echo shell test 3\r\nshell test 3\r\n",
ExpErr: "",
ExpDescription: "",
},
}
for _, c := range cases {
@@ -124,7 +174,7 @@ func TestPostWorkflowHookRunner_Run(t *testing.T) {
Log: logger,
CommandName: "plan",
}
_, desc, err := r.Run(ctx, c.Command, tmpDir)
_, desc, err := r.Run(ctx, c.Command, c.Shell, c.ShellArgs, tmpDir)
if c.ExpErr != "" {
ErrContains(t, c.ExpErr, err)
} else {

View File

@@ -13,17 +13,18 @@ import (
//go:generate pegomock generate --package mocks -o mocks/mock_pre_workflows_hook_runner.go PreWorkflowHookRunner
type PreWorkflowHookRunner interface {
Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, string, error)
Run(ctx models.WorkflowHookCommandContext, command string, shell string, shellArgs string, path string) (string, string, error)
}
type DefaultPreWorkflowHookRunner struct {
OutputHandler jobs.ProjectCommandOutputHandler
}
func (wh DefaultPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, string, error) {
func (wh DefaultPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, shell string, shellArgs string, path string) (string, string, error) {
outputFilePath := filepath.Join(path, "OUTPUT_STATUS_FILE")
cmd := exec.Command("sh", "-c", command) // #nosec
shellArgsSlice := append(strings.Split(shellArgs, " "), command)
cmd := exec.Command(shell, shellArgsSlice...) // #nosec
cmd.Dir = path
baseEnvVars := os.Environ()
@@ -58,7 +59,7 @@ func (wh DefaultPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext
wh.OutputHandler.SendWorkflowHook(ctx, "\n", true)
if err != nil {
err = fmt.Errorf("%s: running %q in %q: \n%s", err, command, path, out)
err = fmt.Errorf("%s: running %q in %q: \n%s", err, shell+" "+shellArgs+" "+command, path, out)
ctx.Log.Debug("error: %s", err)
return string(out), "", err
}
@@ -70,12 +71,12 @@ func (wh DefaultPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext
var customStatusErr error
customStatusOut, customStatusErr = os.ReadFile(outputFilePath)
if customStatusErr != nil {
err = fmt.Errorf("%s: running %q in %q: \n%s", err, command, path, out)
err = fmt.Errorf("%s: running %q in %q: \n%s", err, shell+" "+shellArgs+" "+command, path, out)
ctx.Log.Debug("error: %s", err)
return string(out), "", err
}
}
ctx.Log.Info("successfully ran %q in %q", command, path)
return outString, strings.Trim(string(customStatusOut), "\n"), nil
ctx.Log.Info("successfully ran %q in %q", shell+" "+shellArgs+" "+command, path)
return string(out), strings.Trim(string(customStatusOut), "\n"), nil
}

View File

@@ -15,72 +15,122 @@ import (
)
func TestPreWorkflowHookRunner_Run(t *testing.T) {
defaultShell := "sh"
defaultShellArgs := "-c"
cases := []struct {
Command string
Shell string
ShellArgs string
ExpOut string
ExpErr string
ExpDescription string
}{
{
Command: "",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo hi",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "hi\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: `printf \'your main.tf file does not provide default region.\\ncheck\'`,
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: `'your`,
ExpErr: "",
ExpDescription: "",
},
{
Command: `printf 'your main.tf file does not provide default region.\ncheck'`,
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "your main.tf file does not provide default region.\r\ncheck",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo 'a",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "sh: 1: Syntax error: Unterminated quoted string\r\n",
ExpErr: "exit status 2: running \"echo 'a\" in",
ExpErr: "exit status 2: running \"sh -c echo 'a\" in",
ExpDescription: "",
},
{
Command: "echo hi >> file && cat file",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "hi\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "lkjlkj",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "sh: 1: lkjlkj: not found\r\n",
ExpErr: "exit status 127: running \"lkjlkj\" in",
ExpErr: "exit status 127: running \"sh -c lkjlkj\" in",
ExpDescription: "",
},
{
Command: "echo base_repo_name=$BASE_REPO_NAME base_repo_owner=$BASE_REPO_OWNER head_repo_name=$HEAD_REPO_NAME head_repo_owner=$HEAD_REPO_OWNER head_branch_name=$HEAD_BRANCH_NAME head_commit=$HEAD_COMMIT base_branch_name=$BASE_BRANCH_NAME pull_num=$PULL_NUM pull_url=$PULL_URL pull_author=$PULL_AUTHOR",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "base_repo_name=basename base_repo_owner=baseowner head_repo_name=headname head_repo_owner=headowner head_branch_name=add-feat head_commit=12345abcdef base_branch_name=main pull_num=2 pull_url=https://github.com/runatlantis/atlantis/pull/2 pull_author=acme\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo user_name=$USER_NAME",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "user_name=acme-user\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo something > $OUTPUT_STATUS_FILE",
Shell: defaultShell,
ShellArgs: defaultShellArgs,
ExpOut: "",
ExpErr: "",
ExpDescription: "something",
},
{
Command: "echo shell test 1",
Shell: "bash",
ShellArgs: defaultShellArgs,
ExpOut: "shell test 1\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo shell test 2",
Shell: defaultShell,
ShellArgs: "-cx",
ExpOut: "+ echo shell test 2\r\nshell test 2\r\n",
ExpErr: "",
ExpDescription: "",
},
{
Command: "echo shell test 3",
Shell: "bash",
ShellArgs: "-cv",
ExpOut: "echo shell test 3\r\nshell test 3\r\n",
ExpErr: "",
ExpDescription: "",
},
}
for _, c := range cases {
@@ -124,7 +174,7 @@ func TestPreWorkflowHookRunner_Run(t *testing.T) {
Log: logger,
CommandName: "plan",
}
_, desc, err := r.Run(ctx, c.Command, tmpDir)
_, desc, err := r.Run(ctx, c.Command, c.Shell, c.ShellArgs, tmpDir)
if c.ExpErr != "" {
ErrContains(t, c.ExpErr, err)
} else {

View File

@@ -109,6 +109,16 @@ func (w *DefaultPostWorkflowHooksCommandRunner) runHooks(
}
ctx.HookID = uuid.NewString()
shell := hook.Shell
if shell == "" {
ctx.Log.Debug("Setting shell to default: %q", shell)
shell = "sh"
}
shellArgs := hook.ShellArgs
if shellArgs == "" {
ctx.Log.Debug("Setting shellArgs to default: %q", shellArgs)
shellArgs = "-c"
}
url, err := w.Router.GenerateProjectWorkflowHookURL(ctx.HookID)
if err != nil {
return err
@@ -118,7 +128,7 @@ func (w *DefaultPostWorkflowHooksCommandRunner) runHooks(
ctx.Log.Warn("unable to update post workflow hook status: %s", err)
}
_, runtimeDesc, err := w.PostWorkflowHookRunner.Run(ctx, hook.RunCommand, repoDir)
_, runtimeDesc, err := w.PostWorkflowHookRunner.Run(ctx, hook.RunCommand, shell, shellArgs, repoDir)
if err != nil {
if err := w.CommitStatusUpdater.UpdatePostWorkflowHook(ctx.Pull, models.FailedCommitStatus, hookDescription, runtimeDesc, url); err != nil {

View File

@@ -57,11 +57,33 @@ func TestRunPostHooks_Clone(t *testing.T) {
Log: log,
}
defaultShell := "sh"
defaultShellArgs := "-c"
testHook := valid.WorkflowHook{
StepName: "test",
RunCommand: "some command",
}
testHookWithShell := valid.WorkflowHook{
StepName: "test1",
RunCommand: "echo test1",
Shell: "bash",
}
testHookWithShellArgs := valid.WorkflowHook{
StepName: "test2",
RunCommand: "echo test2",
ShellArgs: "-ce",
}
testHookWithShellandShellArgs := valid.WorkflowHook{
StepName: "test3",
RunCommand: "echo test3",
Shell: "bash",
ShellArgs: "-ce",
}
repoDir := "path/to/repo"
result := "some result"
runtimeDesc := ""
@@ -104,11 +126,14 @@ func TestRunPostHooks_Clone(t *testing.T) {
When(postWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(postWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(pCtx, testHook.RunCommand, repoDir)).ThenReturn(result, runtimeDesc, nil)
When(whPostWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Any[string](),
Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := postWh.RunPostHooks(ctx, cmd)
Ok(t, err)
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
t.Run("success hooks not in cfg", func(t *testing.T) {
@@ -136,7 +161,8 @@ func TestRunPostHooks_Clone(t *testing.T) {
Ok(t, err)
whPostWorkflowHookRunner.VerifyWasCalled(Never()).Run(pCtx, testHook.RunCommand, repoDir)
whPostWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](),
Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
postWhWorkingDirLocker.VerifyWasCalled(Never()).TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, "path")
postWhWorkingDir.VerifyWasCalled(Never()).Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)
})
@@ -162,7 +188,8 @@ func TestRunPostHooks_Clone(t *testing.T) {
Assert(t, err != nil, "error not nil")
postWhWorkingDir.VerifyWasCalled(Never()).Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)
whPostWorkflowHookRunner.VerifyWasCalled(Never()).Run(pCtx, testHook.RunCommand, repoDir)
whPostWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](),
Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
})
t.Run("error cloning", func(t *testing.T) {
@@ -193,7 +220,8 @@ func TestRunPostHooks_Clone(t *testing.T) {
Assert(t, err != nil, "error not nil")
whPostWorkflowHookRunner.VerifyWasCalled(Never()).Run(pCtx, testHook.RunCommand, repoDir)
whPostWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](),
Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
@@ -220,7 +248,8 @@ func TestRunPostHooks_Clone(t *testing.T) {
When(postWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(postWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))).ThenReturn(result, runtimeDesc, errors.New("some error"))
When(whPostWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand),
Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, errors.New("some error"))
err := postWh.RunPostHooks(ctx, cmd)
@@ -259,12 +288,117 @@ func TestRunPostHooks_Clone(t *testing.T) {
When(postWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(postWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
When(whPostWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand),
Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := postWh.RunPostHooks(ctx, cmd)
Ok(t, err)
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
t.Run("shell passed to webhooks", func(t *testing.T) {
postWorkflowHooksSetup(t)
var unlockCalled = newBool(false)
unlockFn := func() {
unlockCalled = newBool(true)
}
globalCfg := valid.GlobalCfg{
Repos: []valid.Repo{
{
ID: testdata.GithubRepo.ID(),
PostWorkflowHooks: []*valid.WorkflowHook{
&testHookWithShell,
},
},
},
}
postWh.GlobalCfg = globalCfg
When(postWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(postWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHookWithShell.RunCommand),
Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := postWh.RunPostHooks(ctx, cmd)
Ok(t, err)
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHookWithShell.RunCommand), Eq(testHookWithShell.Shell), Eq(defaultShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
t.Run("shellArgs passed to webhooks", func(t *testing.T) {
postWorkflowHooksSetup(t)
var unlockCalled = newBool(false)
unlockFn := func() {
unlockCalled = newBool(true)
}
globalCfg := valid.GlobalCfg{
Repos: []valid.Repo{
{
ID: testdata.GithubRepo.ID(),
PostWorkflowHooks: []*valid.WorkflowHook{
&testHookWithShellArgs,
},
},
},
}
postWh.GlobalCfg = globalCfg
When(postWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(postWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand),
Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := postWh.RunPostHooks(ctx, cmd)
Ok(t, err)
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHookWithShellArgs.RunCommand), Eq(defaultShell), Eq(testHookWithShellArgs.ShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
t.Run("Shell and ShellArgs passed to webhooks", func(t *testing.T) {
postWorkflowHooksSetup(t)
var unlockCalled = newBool(false)
unlockFn := func() {
unlockCalled = newBool(true)
}
globalCfg := valid.GlobalCfg{
Repos: []valid.Repo{
{
ID: testdata.GithubRepo.ID(),
PostWorkflowHooks: []*valid.WorkflowHook{
&testHookWithShellandShellArgs,
},
},
},
}
postWh.GlobalCfg = globalCfg
When(postWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(postWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](),
Eq(testHookWithShellandShellArgs.RunCommand), Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := postWh.RunPostHooks(ctx, cmd)
Ok(t, err)
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHookWithShellandShellArgs.RunCommand), Eq(testHookWithShellandShellArgs.Shell), Eq(testHookWithShellandShellArgs.ShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
}

View File

@@ -106,6 +106,16 @@ func (w *DefaultPreWorkflowHooksCommandRunner) runHooks(
}
ctx.HookID = uuid.NewString()
shell := hook.Shell
if shell == "" {
ctx.Log.Debug("Setting shell to default: %q", shell)
shell = "sh"
}
shellArgs := hook.ShellArgs
if shellArgs == "" {
ctx.Log.Debug("Setting shellArgs to default: %q", shellArgs)
shellArgs = "-c"
}
url, err := w.Router.GenerateProjectWorkflowHookURL(ctx.HookID)
if err != nil {
return err
@@ -116,7 +126,7 @@ func (w *DefaultPreWorkflowHooksCommandRunner) runHooks(
return err
}
_, runtimeDesc, err := w.PreWorkflowHookRunner.Run(ctx, hook.RunCommand, repoDir)
_, runtimeDesc, err := w.PreWorkflowHookRunner.Run(ctx, hook.RunCommand, shell, shellArgs, repoDir)
if err != nil {
if err := w.CommitStatusUpdater.UpdatePreWorkflowHook(ctx.Pull, models.FailedCommitStatus, hookDescription, runtimeDesc, url); err != nil {

View File

@@ -60,11 +60,33 @@ func TestRunPreHooks_Clone(t *testing.T) {
Log: log,
}
defaultShell := "sh"
defaultShellArgs := "-c"
testHook := valid.WorkflowHook{
StepName: "test",
RunCommand: "some command",
}
testHookWithShell := valid.WorkflowHook{
StepName: "test1",
RunCommand: "echo test1",
Shell: "bash",
}
testHookWithShellArgs := valid.WorkflowHook{
StepName: "test2",
RunCommand: "echo test2",
ShellArgs: "-ce",
}
testHookWithShellandShellArgs := valid.WorkflowHook{
StepName: "test3",
RunCommand: "echo test3",
Shell: "bash",
ShellArgs: "-ce",
}
repoDir := "path/to/repo"
result := "some result"
runtimeDesc := ""
@@ -106,12 +128,14 @@ func TestRunPreHooks_Clone(t *testing.T) {
When(preWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(preWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand),
Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := preWh.RunPreHooks(ctx, cmd)
Ok(t, err)
whPreWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))
whPreWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
@@ -140,7 +164,7 @@ func TestRunPreHooks_Clone(t *testing.T) {
Ok(t, err)
whPreWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))
whPreWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
preWhWorkingDirLocker.VerifyWasCalled(Never()).TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, "")
preWhWorkingDir.VerifyWasCalled(Never()).Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)
})
@@ -167,7 +191,7 @@ func TestRunPreHooks_Clone(t *testing.T) {
Assert(t, err != nil, "error not nil")
preWhWorkingDir.VerifyWasCalled(Never()).Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)
whPreWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))
whPreWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
})
t.Run("error cloning", func(t *testing.T) {
@@ -198,7 +222,7 @@ func TestRunPreHooks_Clone(t *testing.T) {
Assert(t, err != nil, "error not nil")
whPreWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))
whPreWorkflowHookRunner.VerifyWasCalled(Never()).Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
@@ -225,7 +249,8 @@ func TestRunPreHooks_Clone(t *testing.T) {
When(preWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(preWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))).ThenReturn(result, runtimeDesc, errors.New("some error"))
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand),
Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, errors.New("some error"))
err := preWh.RunPreHooks(ctx, cmd)
@@ -264,12 +289,115 @@ func TestRunPreHooks_Clone(t *testing.T) {
When(preWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(preWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := preWh.RunPreHooks(ctx, cmd)
Ok(t, err)
whPreWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(repoDir))
whPreWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand), Eq(defaultShell), Eq(defaultShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
t.Run("shell passed to webhooks", func(t *testing.T) {
preWorkflowHooksSetup(t)
var unlockCalled = newBool(false)
unlockFn := func() {
unlockCalled = newBool(true)
}
globalCfg := valid.GlobalCfg{
Repos: []valid.Repo{
{
ID: testdata.GithubRepo.ID(),
PreWorkflowHooks: []*valid.WorkflowHook{
&testHookWithShell,
},
},
},
}
preWh.GlobalCfg = globalCfg
When(preWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(preWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHookWithShell.RunCommand),
Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := preWh.RunPreHooks(ctx, cmd)
Ok(t, err)
whPreWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHookWithShell.RunCommand), Eq(testHookWithShell.Shell), Eq(defaultShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
t.Run("shellArgs passed to webhooks", func(t *testing.T) {
preWorkflowHooksSetup(t)
var unlockCalled = newBool(false)
unlockFn := func() {
unlockCalled = newBool(true)
}
globalCfg := valid.GlobalCfg{
Repos: []valid.Repo{
{
ID: testdata.GithubRepo.ID(),
PreWorkflowHooks: []*valid.WorkflowHook{
&testHookWithShellArgs,
},
},
},
}
preWh.GlobalCfg = globalCfg
When(preWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(preWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](), Eq(testHook.RunCommand),
Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := preWh.RunPreHooks(ctx, cmd)
Ok(t, err)
whPreWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHookWithShellArgs.RunCommand), Eq(defaultShell), Eq(testHookWithShellArgs.ShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
t.Run("Shell and ShellArgs passed to webhooks", func(t *testing.T) {
preWorkflowHooksSetup(t)
var unlockCalled = newBool(false)
unlockFn := func() {
unlockCalled = newBool(true)
}
globalCfg := valid.GlobalCfg{
Repos: []valid.Repo{
{
ID: testdata.GithubRepo.ID(),
PreWorkflowHooks: []*valid.WorkflowHook{
&testHookWithShellandShellArgs,
},
},
},
}
preWh.GlobalCfg = globalCfg
When(preWhWorkingDirLocker.TryLock(testdata.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(preWhWorkingDir.Clone(log, testdata.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPreWorkflowHookRunner.Run(Any[models.WorkflowHookCommandContext](),
Eq(testHookWithShellandShellArgs.RunCommand), Any[string](), Any[string](), Eq(repoDir))).ThenReturn(result, runtimeDesc, nil)
err := preWh.RunPreHooks(ctx, cmd)
Ok(t, err)
whPreWorkflowHookRunner.VerifyWasCalledOnce().Run(Any[models.WorkflowHookCommandContext](),
Eq(testHookWithShellandShellArgs.RunCommand), Eq(testHookWithShellandShellArgs.Shell),
Eq(testHookWithShellandShellArgs.ShellArgs), Eq(repoDir))
Assert(t, *unlockCalled == true, "unlock function called")
})
}