diff --git a/.github/workflows/selftest.yml b/.github/workflows/selftest.yml index 94ed391..e4f5442 100644 --- a/.github/workflows/selftest.yml +++ b/.github/workflows/selftest.yml @@ -11,7 +11,7 @@ jobs: URL: ${{ vars.URL }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: ./install-cli with: @@ -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 @@ -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 diff --git a/README.md b/README.md index 53ac687..f3c729f 100644 --- a/README.md +++ b/README.md @@ -25,20 +25,29 @@ Use this GitHub Action to automatically submit each PR's changes to [Overmind](h
Not using GitHub? - 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: 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. +

@@ -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 @@ -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. diff --git a/end-change/action.yml b/end-change/action.yml index 3463575..4e10db2 100644 --- a/end-change/action.yml +++ b/end-change/action.yml @@ -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 @@ -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 diff --git a/start-change/action.yml b/start-change/action.yml index ce83367..8ff2b3d 100644 --- a/start-change/action.yml +++ b/start-change/action.yml @@ -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 @@ -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 diff --git a/submit-plan/action.yml b/submit-plan/action.yml index 7cdb4ee..ba30c11 100644 --- a/submit-plan/action.yml +++ b/submit-plan/action.yml @@ -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 }} @@ -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" @@ -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 @@ -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}} )" @@ -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 @@ -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 @@ -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