Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add COMMENT_ARGS to Pre/Post-Workflow Hook Execution Environment #2621

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions runatlantis.io/docs/post-workflow-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,6 @@ command](custom-workflows.html#custom-run-command).
* `PULL_AUTHOR` - Username of the pull request author, ex. `acme-user`.
* `DIR` - The absolute path to the root of the cloned repository.
* `USER_NAME` - Username of the VCS user running command, ex. `acme-user`. During an autoplan, the user will be the Atlantis API user, ex. `atlantis`.
* `COMMENT_ARGS` - Any additional flags passed in the comment on the pull request. Flags are separated by commas and
every character is escaped, ex. `atlantis plan -- arg1 arg2` will result in `COMMENT_ARGS=\a\r\g\1,\a\r\g\2`.
:::
2 changes: 2 additions & 0 deletions runatlantis.io/docs/pre-workflow-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,7 @@ command](custom-workflows.html#custom-run-command).
* `PULL_AUTHOR` - Username of the pull request author, ex. `acme-user`.
* `DIR` - The absolute path to the root of the cloned repository.
* `USER_NAME` - Username of the VCS user running command, ex. `acme-user`. During an autoplan, the user will be the Atlantis API user, ex. `atlantis`.
* `COMMENT_ARGS` - Any additional flags passed in the comment on the pull request. Flags are separated by commas and
every character is escaped, ex. `atlantis plan -- arg1 arg2` will result in `COMMENT_ARGS=\a\r\g\1,\a\r\g\2`.
:::

2 changes: 2 additions & 0 deletions server/core/runtime/post_workflow_hook_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"os/exec"
"strings"

"github.com/runatlantis/atlantis/server/events/models"
)
Expand All @@ -24,6 +25,7 @@ func (wh DefaultPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContex
"BASE_BRANCH_NAME": ctx.Pull.BaseBranch,
"BASE_REPO_NAME": ctx.BaseRepo.Name,
"BASE_REPO_OWNER": ctx.BaseRepo.Owner,
"COMMENT_ARGS": strings.Join(ctx.EscapedCommentArgs, ","),
"DIR": path,
"HEAD_BRANCH_NAME": ctx.Pull.HeadBranch,
"HEAD_COMMIT": ctx.Pull.HeadCommit,
Expand Down
2 changes: 2 additions & 0 deletions server/core/runtime/pre_workflow_hook_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"os/exec"
"strings"

"github.com/runatlantis/atlantis/server/events/models"
)
Expand All @@ -24,6 +25,7 @@ func (wh DefaultPreWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext
"BASE_BRANCH_NAME": ctx.Pull.BaseBranch,
"BASE_REPO_NAME": ctx.BaseRepo.Name,
"BASE_REPO_OWNER": ctx.BaseRepo.Owner,
"COMMENT_ARGS": strings.Join(ctx.EscapedCommentArgs, ","),
"DIR": path,
"HEAD_BRANCH_NAME": ctx.Pull.HeadBranch,
"HEAD_COMMIT": ctx.Pull.HeadCommit,
Expand Down
8 changes: 4 additions & 4 deletions server/events/command_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func (c *DefaultCommandRunner) RunAutoplanCommand(baseRepo models.Repo, headRepo
return
}

err = c.PreWorkflowHooksCommandRunner.RunPreHooks(ctx)
err = c.PreWorkflowHooksCommandRunner.RunPreHooks(ctx, nil)

if err != nil {
ctx.Log.Err("Error running pre-workflow hooks %s. Proceeding with %s command.", err, command.Plan)
Expand All @@ -173,7 +173,7 @@ func (c *DefaultCommandRunner) RunAutoplanCommand(baseRepo models.Repo, headRepo

autoPlanRunner.Run(ctx, nil)

err = c.PostWorkflowHooksCommandRunner.RunPostHooks(ctx)
err = c.PostWorkflowHooksCommandRunner.RunPostHooks(ctx, nil)

if err != nil {
ctx.Log.Err("Error running post-workflow hooks %s.", err)
Expand Down Expand Up @@ -285,7 +285,7 @@ func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHead
return
}

err = c.PreWorkflowHooksCommandRunner.RunPreHooks(ctx)
err = c.PreWorkflowHooksCommandRunner.RunPreHooks(ctx, cmd)

if err != nil {
ctx.Log.Err("Error running pre-workflow hooks %s. Proceeding with %s command.", err, cmd.Name.String())
Expand All @@ -295,7 +295,7 @@ func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHead

cmdRunner.Run(ctx, cmd)

err = c.PostWorkflowHooksCommandRunner.RunPostHooks(ctx)
err = c.PostWorkflowHooksCommandRunner.RunPostHooks(ctx, cmd)

if err != nil {
ctx.Log.Err("Error running post-workflow hooks %s.", err)
Expand Down
4 changes: 2 additions & 2 deletions server/events/command_runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,11 @@ func setup(t *testing.T) *vcsmocks.MockClient {

preWorkflowHooksCommandRunner = mocks.NewMockPreWorkflowHooksCommandRunner()

When(preWorkflowHooksCommandRunner.RunPreHooks(matchers.AnyPtrToEventsCommandContext())).ThenReturn(nil)
When(preWorkflowHooksCommandRunner.RunPreHooks(matchers.AnyPtrToEventsCommandContext(), matchers.AnyPtrToEventsCommentCommand())).ThenReturn(nil)

postWorkflowHooksCommandRunner = mocks.NewMockPostWorkflowHooksCommandRunner()

When(postWorkflowHooksCommandRunner.RunPostHooks(matchers.AnyPtrToEventsCommandContext())).ThenReturn(nil)
When(postWorkflowHooksCommandRunner.RunPostHooks(matchers.AnyPtrToEventsCommandContext(), matchers.AnyPtrToEventsCommentCommand())).ThenReturn(nil)

globalCfg := valid.NewGlobalCfgFromArgs(valid.GlobalCfgArgs{})
scope, _, _ := metrics.NewLoggingScope(logger, "atlantis")
Expand Down
23 changes: 14 additions & 9 deletions server/events/mocks/mock_post_workflows_hooks_command_runner.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 13 additions & 8 deletions server/events/mocks/mock_pre_workflows_hooks_command_runner.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions server/events/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,4 +510,9 @@ type WorkflowHookCommandContext struct {
User User
// Verbose is true when the user would like verbose output.
Verbose bool
// EscapedCommentArgs are the extra arguments that were added to the atlantis
// command, ex. atlantis plan -- -target=resource. We then escape them
// by adding a \ before each character so that they can be used within
// sh -c safely, i.e. sh -c "terraform plan $(touch bad)".
EscapedCommentArgs []string
}
22 changes: 14 additions & 8 deletions server/events/post_workflow_hooks_command_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_post_workflows_hooks_command_runner.go PostWorkflowHooksCommandRunner

type PostWorkflowHooksCommandRunner interface {
RunPostHooks(ctx *command.Context) error
RunPostHooks(ctx *command.Context, cmd *CommentCommand) error
}

// DefaultPostWorkflowHooksCommandRunner is the first step when processing a workflow hook commands.
Expand All @@ -25,7 +25,7 @@ type DefaultPostWorkflowHooksCommandRunner struct {

// RunPostHooks runs post_workflow_hooks after a plan/apply has completed
func (w *DefaultPostWorkflowHooksCommandRunner) RunPostHooks(
ctx *command.Context,
ctx *command.Context, cmd *CommentCommand,
) error {
pull := ctx.Pull
baseRepo := pull.BaseRepo
Expand Down Expand Up @@ -59,14 +59,20 @@ func (w *DefaultPostWorkflowHooksCommandRunner) RunPostHooks(
return err
}

var escapedArgs []string
if cmd != nil {
escapedArgs = escapeArgs(cmd.Flags)
}

err = w.runHooks(
models.WorkflowHookCommandContext{
BaseRepo: baseRepo,
HeadRepo: headRepo,
Log: log,
Pull: pull,
User: user,
Verbose: false,
BaseRepo: baseRepo,
HeadRepo: headRepo,
Log: log,
Pull: pull,
User: user,
Verbose: false,
EscapedCommentArgs: escapedArgs,
},
postWorkflowHooks, repoDir)

Expand Down
49 changes: 44 additions & 5 deletions server/events/post_workflow_hooks_command_runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func TestRunPostHooks_Clone(t *testing.T) {
When(postWhWorkingDir.Clone(log, fixtures.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(pCtx, testHook.RunCommand, repoDir)).ThenReturn(result, nil)

err := postWh.RunPostHooks(ctx)
err := postWh.RunPostHooks(ctx, nil)

Ok(t, err)
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(pCtx, testHook.RunCommand, repoDir)
Expand All @@ -121,7 +121,7 @@ func TestRunPostHooks_Clone(t *testing.T) {

postWh.GlobalCfg = globalCfg

err := postWh.RunPostHooks(ctx)
err := postWh.RunPostHooks(ctx, nil)

Ok(t, err)

Expand All @@ -147,7 +147,7 @@ func TestRunPostHooks_Clone(t *testing.T) {

When(postWhWorkingDirLocker.TryLock(fixtures.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(func() {}, errors.New("some error"))

err := postWh.RunPostHooks(ctx)
err := postWh.RunPostHooks(ctx, nil)

Assert(t, err != nil, "error not nil")
postWhWorkingDir.VerifyWasCalled(Never()).Clone(log, fixtures.GithubRepo, newPull, events.DefaultWorkspace)
Expand Down Expand Up @@ -178,7 +178,7 @@ func TestRunPostHooks_Clone(t *testing.T) {
When(postWhWorkingDirLocker.TryLock(fixtures.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(postWhWorkingDir.Clone(log, fixtures.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, errors.New("some error"))

err := postWh.RunPostHooks(ctx)
err := postWh.RunPostHooks(ctx, nil)

Assert(t, err != nil, "error not nil")

Expand Down Expand Up @@ -211,9 +211,48 @@ func TestRunPostHooks_Clone(t *testing.T) {
When(postWhWorkingDir.Clone(log, fixtures.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(pCtx, testHook.RunCommand, repoDir)).ThenReturn(result, errors.New("some error"))

err := postWh.RunPostHooks(ctx)
err := postWh.RunPostHooks(ctx, nil)

Assert(t, err != nil, "error not nil")
Assert(t, *unlockCalled == true, "unlock function called")
})

t.Run("comment args passed to webhooks", func(t *testing.T) {
postWorkflowHooksSetup(t)

unlockCalled := newBool(false)
unlockFn := func() {
unlockCalled = newBool(true)
}

globalCfg := valid.GlobalCfg{
Repos: []valid.Repo{
{
ID: fixtures.GithubRepo.ID(),
PostWorkflowHooks: []*valid.WorkflowHook{
&testHook,
},
},
},
}

cmd := &events.CommentCommand{
Flags: []string{"comment", "args"},
}

expectedCtx := pCtx
expectedCtx.EscapedCommentArgs = []string{"\\c\\o\\m\\m\\e\\n\\t", "\\a\\r\\g\\s"}

postWh.GlobalCfg = globalCfg

When(postWhWorkingDirLocker.TryLock(fixtures.GithubRepo.FullName, newPull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)).ThenReturn(unlockFn, nil)
When(postWhWorkingDir.Clone(log, fixtures.GithubRepo, newPull, events.DefaultWorkspace)).ThenReturn(repoDir, false, nil)
When(whPostWorkflowHookRunner.Run(pCtx, testHook.RunCommand, repoDir)).ThenReturn(result, nil)

err := postWh.RunPostHooks(ctx, cmd)

Ok(t, err)
whPostWorkflowHookRunner.VerifyWasCalledOnce().Run(expectedCtx, testHook.RunCommand, repoDir)
Assert(t, *unlockCalled == true, "unlock function called")
})
}
Loading