Skip to content
Open
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
10 changes: 5 additions & 5 deletions .github/workflows/selftest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
URL: ${{ vars.URL }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: ./install-cli
with:
Expand All @@ -26,12 +26,12 @@ jobs:

- name: Cache Terraform Providers
id: cache-terraform
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ./testmodule/.terraform
key: ${{ runner.os }}-${{ hashFiles('./testmodule/.terraform.lock.hcl') }}

- uses: hashicorp/setup-terraform@v3
- uses: hashicorp/setup-terraform@v4
with:
terraform_wrapper: false

Expand All @@ -44,14 +44,14 @@ jobs:
terraform show -json tfplan > tfplan.json

- name: Upload Example GitHub Data
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: ${{ !env.ACT }} # skip during local actions testing
with:
name: github_event.json
path: ${{ github.event_path }}

- name: Upload Example Plan Data
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: ${{ !env.ACT }} # skip during local actions testing
with:
name: tfplan.json
Expand Down
46 changes: 40 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,29 @@ Use this GitHub Action to automatically submit each PR's changes to [Overmind](h
<details>
<summary>Not using GitHub?</summary>

Currently we only have an action for GitHub, but don't fear! We have a CLI that you can use to integrate your own CI tooling:
We have a CLI that you can use to integrate your own CI tooling:

1. Download the CLI from here: <https://github.com/overmindtech/cli/releases>
2. Set the `OVM_API_KEY` environment variable to your API Key
3. Add a step to your pipeline to create a change:
3. Submit a plan:

```shell
./overmind changes submit-plan \
--title 'Pull request title goes here' \
--description 'PR description goes here' \
--comment \
--ticket-link 'link to PR goes here' \
--plan-json 'path/to/plan.json'
tfplan.json
```

For parallel planning workflows (multiple plans per change), use `--no-start` on each plan and `start-analysis` to trigger analysis once:

```shell
./overmind changes submit-plan --no-start --ticket-link "$PR_URL" plan1.json
./overmind changes submit-plan --no-start --ticket-link "$PR_URL" plan2.json
./overmind changes start-analysis --comment --ticket-link "$PR_URL"
```

See the [custom integrations docs](https://docs.overmind.tech/integrations/build_your_own) for more details.

</details>
</br>

Expand All @@ -56,7 +65,7 @@ The `install` action installs the [`overmind`](https://github.com/overmindtech/c
github-api-url: https://ghe.company.com/api/v3 # API for GitHub Enterprise Server (optional)
```

The `submit-plan` action takes a JSON-formatted terraform plan, creates a Overmind Change for it, and runs Impact Analysis.
The `submit-plan` action takes a JSON-formatted terraform plan, creates an Overmind Change for it, and runs Impact Analysis. When the [Overmind GitHub App](https://docs.overmind.tech/integrations/github_app) is installed, the action exits immediately and the App posts results asynchronously as a PR comment. Without the App, it falls back to polling and posting a sticky comment.

```yaml
- uses: overmindtech/actions/submit-plan@main
Expand All @@ -66,6 +75,31 @@ The `submit-plan` action takes a JSON-formatted terraform plan, creates a Overmi
plan-json: ./tfplan.json # Location of the plan in JSON format
```

## Inputs

| Input | Default | Description |
| --- | --- | --- |
| `ovm-api-key` | (required) | Overmind API key. |
| `plan-json` | `tfplan.json` | Path to JSON plan file(s). Space-separated for multiple files. |
| `plan-output` | `tfplan.output` | Path to rendered plan output (`terraform plan \| tee FILE`). |
| `comment` | `"true"` | Post results as a PR comment. Uses GitHub App when installed, falls back to sticky comment. |
| `wait` | `"false"` | Block until analysis completes and populate the `message` output. |
| `tags` | | Comma-separated key=value tags. |
| `comment-header` | `change` | Sticky comment header (use different values for multiple plans on same PR). |
| `app` | | Overmind instance URL (Enterprise on-prem). |
| `number` | PR number | Pull request number. |
| `log` | `info` | Log level. |

> **Deprecated:** `fetch-change` is deprecated. Use `comment` and `wait` instead.

## Outputs

| Output | Description |
| --- | --- |
| `change-url` | URL of the created change. |
| `message` | Markdown summary (populated when `wait: true` or sticky comment fallback). |
| `github-app-active` | `"true"` when the GitHub App is posting the PR comment. |

## Pre-Mortem Example

Copy this workflow to `.github/workflows/overmind.yml` to run `terraform init`, `terraform plan` and submit the planned changes to Overmind.
Expand Down
12 changes: 11 additions & 1 deletion end-change/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ inputs:
app:
description: "The Overmind instance to connect to. Defaults to `https://app.overmind.tech`"
required: false
wait-for-snapshot:
description: "Wait for the snapshot to complete before returning. Defaults to false."
required: false
default: "false"

runs:
using: composite
Expand All @@ -40,7 +44,13 @@ runs:
app_arg="--app ${{ inputs.app }}"
fi

wait_arg=""
if [ "${{ inputs.wait-for-snapshot }}" = "true" ]; then
wait_arg="--wait-for-snapshot"
fi

./overmindtech/overmind changes end-change \
--ticket-link "$ticket_link" \
--log '${{ inputs.log }}' \
$app_arg
$app_arg \
$wait_arg
12 changes: 11 additions & 1 deletion start-change/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ inputs:
app:
description: "The Overmind instance to connect to. Defaults to `https://app.overmind.tech`"
required: false
wait-for-snapshot:
description: "Wait for the snapshot to complete before returning. Defaults to false."
required: false
default: "false"

runs:
using: composite
Expand All @@ -40,7 +44,13 @@ runs:
app_arg="--app ${{ inputs.app }}"
fi

wait_arg=""
if [ "${{ inputs.wait-for-snapshot }}" = "true" ]; then
wait_arg="--wait-for-snapshot"
fi

./overmindtech/overmind changes start-change \
--ticket-link "$ticket_link" \
--log '${{ inputs.log }}' \
$app_arg
$app_arg \
$wait_arg
135 changes: 105 additions & 30 deletions submit-plan/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ inputs:
log:
description: The log level for the job
default: info
comment:
description: "Post analysis results as a PR comment. Uses the Overmind GitHub App when installed (async, no CI wait), otherwise falls back to a sticky PR comment."
default: "true"
wait:
description: "Block until analysis completes and make results available as the `message` step output. Useful for gating deployments or including results in Slack messages."
default: "false"
fetch-change:
description: Whether or not to fetch the change after submitting it. Set this to 'false' to avoid waiting for risk calculation when no human review is required.
default: true
description: "[Deprecated: use 'comment' and 'wait' instead] Whether or not to fetch the change after submitting it."
required: false
number:
description: "Pull request number to report results back."
default: ${{ github.event.number }}
Expand All @@ -35,6 +41,9 @@ inputs:
tags:
description: "A comma separated list of key=value tags to attach to the change"
required: false
comment-header:
description: "Header for the sticky PR comment (used to distinguish multiple submit-plan calls on the same PR)"
default: change
# ENG-1985, disabled until we decide how manual labels and manual tags should be handled.
# labels:
# description: "A comma separated list of name=color labels to attach to the change"
Expand All @@ -47,7 +56,11 @@ outputs:

message:
description: "A markdown formatted message describing the current state of the change"
value: ${{ steps.submit-plan.outputs.change-url }}
value: ${{ steps.submit-plan.outputs.message }}

github-app-active:
description: "Whether the GitHub App is handling PR commenting for this change"
value: ${{ steps.submit-plan.outputs.github-app-active }}

runs:
using: composite
Expand All @@ -63,6 +76,16 @@ runs:
declare -a args
declare -a chg_args

# Resolve deprecated fetch-change to comment/wait
OVM_COMMENT='${{ inputs.comment }}'
OVM_WAIT='${{ inputs.wait }}'
if [ -n '${{ inputs.fetch-change }}' ]; then
echo "::warning::The 'fetch-change' input is deprecated. Use 'comment' and 'wait' instead."
if [ '${{ inputs.fetch-change }}' = 'false' ]; then
OVM_COMMENT='false'
fi
fi

# figure out the git change in this event
if [ ${{ github.event_name }} = pull_request ]; then
base="$(jq -r .pull_request.base.sha < ${{github.event_path}} )"
Expand All @@ -71,16 +94,6 @@ runs:
elif [ ${{ github.event_name }} = push ]; then
base="$(jq -r .before < ${{github.event_path}} )"
if [ "$base" = "0000000000000000000000000000000000000000" ]; then
# new branch was pushed, default to the repo's default branch as
# base for the diff
#
# Note that this is only the second-worst choice here, as this will
# show what's on the default branch, but not in this push as getting
# removed, even though a merge would keep it. To avoid _that_, we'd
# need to use the `merge-base` between the default branch and the
# current branch, but that would inversely _hide_ changes that are
# on the default branch but not in this push, making it easier to
# miss things for folks with a push-based workflow.
base="${{github.event.repository.default_branch}}"
prefix="origin/"
fi
Expand Down Expand Up @@ -127,25 +140,84 @@ runs:
chg_args+=("--app=${{ inputs.app }}")
fi

./overmindtech/overmind changes submit-plan \
--title "$title" \
--description "$description" \
--ticket-link "$ticket_link" \
"${args[@]}" \
'${{ inputs.plan-json }}' \
> ./overmindtech/change-url
# Submit the plan. When comment is enabled, try --comment for eval-able output.
# Falls back to the old (no --comment) path if the CLI doesn't support it yet.
GITHUB_APP_ACTIVE=false
if [ "$OVM_COMMENT" = "true" ]; then
set +e
SUBMIT_OUTPUT=$(./overmindtech/overmind changes submit-plan \
--comment \
--title "$title" \
--description "$description" \
--ticket-link "$ticket_link" \
"${args[@]}" \
'${{ inputs.plan-json }}' 2>./overmindtech/submit-stderr.log)
SUBMIT_RC=$?
set -e

if [ $SUBMIT_RC -eq 0 ]; then
eval "$SUBMIT_OUTPUT"
elif grep -q "unknown flag" ./overmindtech/submit-stderr.log 2>/dev/null; then
echo "::notice::CLI does not support --comment; falling back to legacy mode"
CHANGE_URL=$(./overmindtech/overmind changes submit-plan \
--title "$title" \
--description "$description" \
--ticket-link "$ticket_link" \
"${args[@]}" \
'${{ inputs.plan-json }}')
else
cat ./overmindtech/submit-stderr.log >&2
exit $SUBMIT_RC
fi
else
CHANGE_URL=$(./overmindtech/overmind changes submit-plan \
--title "$title" \
--description "$description" \
--ticket-link "$ticket_link" \
"${args[@]}" \
'${{ inputs.plan-json }}')
fi

echo "$CHANGE_URL" > ./overmindtech/change-url

echo "ticket-link=${{ github.event.pull_request.html_url }}" >> $GITHUB_OUTPUT
echo "${{ github.event.pull_request.html_url }}" >> ./overmindtech/ticket-link
echo "change-url=$(< ./overmindtech/change-url)" >> $GITHUB_OUTPUT
echo "change-url=$CHANGE_URL" >> $GITHUB_OUTPUT
echo "github-app-active=${GITHUB_APP_ACTIVE:-false}" >> $GITHUB_OUTPUT

# Decide whether to fetch the change summary
should_fetch=false
if [ "$OVM_WAIT" = "true" ]; then
should_fetch=true
elif [ "${GITHUB_APP_ACTIVE:-false}" != "true" ] && [ "$OVM_COMMENT" = "true" ]; then
should_fetch=true
fi

if [ ${{ inputs.fetch-change }} != "false" ]; then
./overmindtech/overmind changes get-change \
--change "$(< ./overmindtech/change-url)" \
if [ "$should_fetch" = "true" ]; then
set +e
GET_OUTPUT=$(./overmindtech/overmind changes get-change \
--change "$CHANGE_URL" \
--wait \
--format markdown \
--log '${{ inputs.log }}' \
"${chg_args[@]}" \
> ./overmindtech/message
"${chg_args[@]}" 2>./overmindtech/get-stderr.log)
GET_RC=$?
set -e

if [ $GET_RC -eq 0 ]; then
echo "$GET_OUTPUT" > ./overmindtech/message
elif grep -q "unknown flag" ./overmindtech/get-stderr.log 2>/dev/null; then
echo "::notice::CLI does not support --wait on get-change; falling back to legacy mode"
./overmindtech/overmind changes get-change \
--change "$CHANGE_URL" \
--format markdown \
--log '${{ inputs.log }}' \
"${chg_args[@]}" \
> ./overmindtech/message
else
cat ./overmindtech/get-stderr.log >&2
exit $GET_RC
fi

DELIMITER="$(uuidgen)"
echo "message<<$DELIMITER" >> $GITHUB_OUTPUT
Expand All @@ -155,20 +227,23 @@ runs:

- name: Post change message as sticky comment
uses: marocchino/sticky-pull-request-comment@v2
if: ${{ steps.submit-plan.outputs.change-url != '' && inputs.fetch-change != 'false' }}
if: >-
${{ steps.submit-plan.outputs.change-url != ''
&& steps.submit-plan.outputs.message != ''
&& steps.submit-plan.outputs.github-app-active != 'true' }}
with:
header: change
header: ${{ inputs.comment-header }}
message: ${{ steps.submit-plan.outputs.message }}
number: ${{ inputs.number }}

- name: Upload change-url as file
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: overmind-change-url.txt
path: ./overmindtech/change-url

- name: Upload ticket-link as file
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: overmind-ticket-link.txt
path: ./overmindtech/ticket-link
Loading