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

feat: Add pre and post workflow hook status #2441

Merged
merged 21 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
11 changes: 7 additions & 4 deletions runatlantis.io/docs/post-workflow-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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` commands.
Post workflow hooks also only allow `run` and `description` commands.

[[toc]]

Expand Down Expand Up @@ -40,6 +40,7 @@ repos:
workflow: myworkflow
post_workflow_hooks:
- run: infracost output --path=/tmp/$BASE_REPO_OWNER-$BASE_REPO_NAME-$PULL_NUM-*-infracost.json --format=github-comment --out-file=/tmp/infracost-comment.md
description: Running infracost
# Now report the output as desired, e.g. post to GitHub as a comment.
# ...
```
Expand All @@ -55,9 +56,10 @@ command](custom-workflows.html#custom-run-command).
- run: custom-command
```

| Key | Type | Default | Required | Description |
| --- | ------ | ------- | -------- | -------------------- |
| run | string | none | no | Run a custom command |
| Key | Type | Default | Required | Description |
| ----------- | ------ | ------- | -------- | --------------------- |
| run | string | none | no | Run a custom command |
| description | string | none | no | Post hook description |

::: tip Notes
* `run` commands are executed with the following environment variables:
Expand All @@ -74,4 +76,5 @@ command](custom-workflows.html#custom-run-command).
* `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`.
* `OUTPUT_STATUS_FILE` - An output file you can use to customize the success or failure status. ex. `echo 'failure' > $OUTPUT_STATUS_FILE`.
Fabianoshz marked this conversation as resolved.
Show resolved Hide resolved
nitrocode marked this conversation as resolved.
Show resolved Hide resolved
:::
11 changes: 7 additions & 4 deletions runatlantis.io/docs/pre-workflow-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ workflows](custom-workflows.html#custom-run-command) in several ways.
present. This 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` commands.
3. Pre workflow hooks only allow `run` and `description` commands.

[[toc]]

Expand All @@ -31,6 +31,7 @@ repos:
- id: /.*/
pre_workflow_hooks:
- run: ./repo-config-generator.sh
description: Generating configs
```
## Reference
### Custom `run` Command
Expand All @@ -39,9 +40,10 @@ command](custom-workflows.html#custom-run-command).
```yaml
- run: custom-command
```
| Key | Type | Default | Required | Description |
| --- | ------ | ------- | -------- | -------------------- |
| run | string | none | no | Run a custom command |
| Key | Type | Default | Required | Description |
| ----------- | ------ | ------- | -------- | -------------------- |
| run | string | none | no | Run a custom command |
| description | string | none | no | Pre hook description |

::: tip Notes
* `run` commands are executed with the following environment variables:
Expand All @@ -58,5 +60,6 @@ command](custom-workflows.html#custom-run-command).
* `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`.
* `OUTPUT_STATUS_FILE` - An output file you can use to customize the success or failure status. ex. `echo 'failure' > $OUTPUT_STATUS_FILE`.
nitrocode marked this conversation as resolved.
Show resolved Hide resolved
:::

8 changes: 8 additions & 0 deletions server/controllers/events/events_controller_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -944,22 +944,30 @@ func setupE2E(t *testing.T, repoDir string) (events_controllers.VCSEventsControl
parallelPoolSize := 1
silenceNoProjects := false

commitStatusUpdater := mocks.NewMockCommitStatusUpdater()

mockPreWorkflowHookRunner = runtimemocks.NewMockPreWorkflowHookRunner()
preWorkflowHookURLGenerator := mocks.NewMockPreWorkflowHookURLGenerator()
preWorkflowHooksCommandRunner := &events.DefaultPreWorkflowHooksCommandRunner{
VCSClient: e2eVCSClient,
GlobalCfg: globalCfg,
WorkingDirLocker: locker,
WorkingDir: workingDir,
PreWorkflowHookRunner: mockPreWorkflowHookRunner,
CommitStatusUpdater: commitStatusUpdater,
Router: preWorkflowHookURLGenerator,
}

mockPostWorkflowHookRunner = runtimemocks.NewMockPostWorkflowHookRunner()
postWorkflowHookURLGenerator := mocks.NewMockPostWorkflowHookURLGenerator()
postWorkflowHooksCommandRunner := &events.DefaultPostWorkflowHooksCommandRunner{
VCSClient: e2eVCSClient,
GlobalCfg: globalCfg,
WorkingDirLocker: locker,
WorkingDir: workingDir,
PostWorkflowHookRunner: mockPostWorkflowHookRunner,
CommitStatusUpdater: commitStatusUpdater,
Router: postWorkflowHookURLGenerator,
}
statsScope, _, _ := metrics.NewLoggingScope(logger, "atlantis")

Expand Down
11 changes: 4 additions & 7 deletions server/core/config/raw/workflow_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,10 @@ func (s WorkflowHook) Validate() error {
func (s WorkflowHook) ToValid() *valid.WorkflowHook {
// This will trigger in case #4 (see WorkflowHook docs).
if len(s.StringVal) > 0 {
// After validation we assume there's only one key and it's a valid
// step name so we just use the first one.
for _, v := range s.StringVal {
return &valid.WorkflowHook{
StepName: RunStepName,
RunCommand: v,
}
return &valid.WorkflowHook{
StepName: RunStepName,
RunCommand: s.StringVal["run"],
StepDescription: s.StringVal["description"],
}
}

Expand Down
5 changes: 3 additions & 2 deletions server/core/config/valid/global_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ type MergedProjectCfg struct {

// WorkflowHook is a map of custom run commands to run before or after workflows.
type WorkflowHook struct {
StepName string
RunCommand string
StepName string
RunCommand string
StepDescription string
}

// DefaultApplyStage is the Atlantis default apply stage.
Expand Down

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

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

19 changes: 11 additions & 8 deletions server/core/runtime/mocks/mock_post_workflows_hook_runner.go

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

19 changes: 11 additions & 8 deletions server/core/runtime/mocks/mock_pre_workflows_hook_runner.go

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

57 changes: 40 additions & 17 deletions server/core/runtime/post_workflow_hook_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,43 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/runatlantis/atlantis/server/events/models"
"github.com/runatlantis/atlantis/server/jobs"
)

//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_post_workflows_hook_runner.go PostWorkflowHookRunner
type PostWorkflowHookRunner interface {
Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, error)
Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, string, error)
}

type DefaultPostWorkflowHookRunner struct{}
type DefaultPostWorkflowHookRunner struct {
OutputHandler jobs.ProjectCommandOutputHandler
}

func (wh DefaultPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, string, error) {
outputFilePath := filepath.Join(path, "OUTPUT_STATUS_FILE")

func (wh DefaultPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContext, command string, path string) (string, error) {
cmd := exec.Command("sh", "-c", command) // #nosec
cmd.Dir = path

baseEnvVars := os.Environ()
customEnvVars := map[string]string{
"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,
"HEAD_REPO_NAME": ctx.HeadRepo.Name,
"HEAD_REPO_OWNER": ctx.HeadRepo.Owner,
"PULL_AUTHOR": ctx.Pull.Author,
"PULL_NUM": fmt.Sprintf("%d", ctx.Pull.Num),
"USER_NAME": ctx.User.Username,
"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,
"HEAD_REPO_NAME": ctx.HeadRepo.Name,
"HEAD_REPO_OWNER": ctx.HeadRepo.Owner,
"PULL_AUTHOR": ctx.Pull.Author,
"PULL_NUM": fmt.Sprintf("%d", ctx.Pull.Num),
"USER_NAME": ctx.User.Username,
"OUTPUT_STATUS_FILE": outputFilePath,
}

finalEnvVars := baseEnvVars
Expand All @@ -47,8 +54,24 @@ func (wh DefaultPostWorkflowHookRunner) Run(ctx models.WorkflowHookCommandContex
if err != nil {
err = fmt.Errorf("%s: running %q in %q: \n%s", err, command, path, out)
ctx.Log.Debug("error: %s", err)
return "", err
return "", "", err
}

// Read the value from the "outputFilePath" file
// to be returned as a custom description.
var customStatusOut []byte
if _, err := os.Stat(outputFilePath); err == nil {
nitrocode marked this conversation as resolved.
Show resolved Hide resolved
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)
ctx.Log.Debug("error: %s", err)
return "", "", err
}
}

wh.OutputHandler.SendWorkflowHook(ctx, fmt.Sprintf("%s\n", string(out)), true)

ctx.Log.Info("successfully ran %q in %q", command, path)
return string(out), nil
return string(out), strings.Trim(string(customStatusOut), "\n"), nil
}
Loading