Add after hooks step #2
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Pipelines | ||
| run-name: Run Gruntwork Pipelines | ||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| # This field can be overriden to customize the runner used for pipelines | ||
| # workflows. | ||
| # | ||
| # IMPORTANT: To use self-hosted runners this workflow must be hosted in | ||
| # the same GitHub organization as your infra-live repository. | ||
| # See https://docs.github.com/en/actions/using-workflows/reusing-workflows#using-self-hosted-runners | ||
| # | ||
| # The value must be an escaped JSON string that will be decoded to the | ||
| # jobs.runs-on field | ||
| # See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on | ||
| # | ||
| # For example: | ||
| # - A simple github runner: "\"ubuntu-22.04\"" | ||
| # - A list of labels: "[\"self-hosted\", \"linux\"]" | ||
| # - A map: "{group: \"ubuntu-runners\", labels: \"ubuntu-20.04-16core\"}" | ||
| runner: | ||
| type: string | ||
| default: '"ubuntu-latest"' | ||
| api_base_url: | ||
| type: string | ||
| default: "https://api.prod.app.gruntwork.io/api/v1" | ||
| pipelines_binary_url: | ||
| type: string | ||
| default: "" | ||
| description: "Override where we fetch pipelines from, used for internal testing" | ||
| pipelines_cli_version: | ||
| type: string | ||
| default: "v0.53.3" | ||
| description: "For Gruntwork internal testing - the version of the pipelines CLI to use" | ||
| pipelines_actions_repo: | ||
| type: string | ||
| default: "gruntwork-io/pipelines-actions" | ||
| description: "Repository to fetch pipelines actions from (e.g. use your org/repo for self-hosted)" | ||
| pipelines_actions_ref: | ||
| type: string | ||
| default: "v4.8.0" | ||
| description: "For Gruntwork internal testing - the ref of the pipelines actions to use" | ||
| pipelines_credentials_repo: | ||
| type: string | ||
| default: "gruntwork-io/pipelines-credentials" | ||
| description: "Repository to fetch pipelines credentials from (e.g. use your org/repo for self-hosted)" | ||
| pipelines_credentials_ref: | ||
| type: string | ||
| default: "v2.0.0" | ||
| description: "For Gruntwork internal testing - the ref of the pipelines credentials to use" | ||
| secrets: | ||
| PIPELINES_READ_TOKEN: | ||
| required: false | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: | ||
| required: false | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: | ||
| required: false | ||
| PR_CREATE_TOKEN: | ||
| required: false | ||
| env: | ||
| PIPELINES_CLI_VERSION: ${{ inputs.pipelines_cli_version }} | ||
| PIPELINES_ACTIONS_REPO: ${{ inputs.pipelines_actions_repo }} | ||
| PIPELINES_ACTIONS_REF: ${{ inputs.pipelines_actions_ref }} | ||
| PIPELINES_CREDENTIALS_REPO: ${{ inputs.pipelines_credentials_repo }} | ||
| PIPELINES_CREDENTIALS_REF: ${{ inputs.pipelines_credentials_ref }} | ||
| # GitHub Actions tends to hit resource exhaustion and kill running jobs | ||
| # if we leave parallelism unbounded, so we set the max to 10 for a sane default. | ||
| TG_PARALLELISM: 10 | ||
| jobs: | ||
| pipelines_orchestrate: | ||
| name: Detect Infrastructure Changes | ||
| runs-on: ${{ fromJSON(inputs.runner) }} | ||
| env: | ||
| JOB_NAME: "Detect Infrastructure Changes" | ||
| steps: | ||
| - name: Record workflow env vars | ||
| env: | ||
| PIPELINES_BINARY_URL: ${{ inputs.pipelines_binary_url }} | ||
| run: | | ||
| time_now=$(date -u +"%s") | ||
| echo "PIPELINES_JOB_START_TIME=$time_now" >> $GITHUB_ENV | ||
| echo "PIPELINES_BINARY_URL=$PIPELINES_BINARY_URL" >> $GITHUB_ENV | ||
| - name: Checkout Pipelines Credentials | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: pipelines-credentials | ||
| repository: ${{ env.PIPELINES_CREDENTIALS_REPO }} | ||
| ref: ${{ env.PIPELINES_CREDENTIALS_REF }} | ||
| - name: Fetch Pipeline Tokens | ||
| id: pipelines-tokens | ||
| uses: ./pipelines-credentials | ||
| env: | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ secrets.PIPELINES_GRUNTWORK_READ_TOKEN || secrets.PIPELINES_READ_TOKEN }} | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: ${{ secrets.PIPELINES_CUSTOMER_ORG_READ_TOKEN || secrets.PIPELINES_READ_TOKEN }} | ||
| PR_CREATE_TOKEN: ${{ secrets.PR_CREATE_TOKEN }} | ||
| with: | ||
| token_requests: | | ||
| [ | ||
| {"name": "gruntwork_read", "path": "pipelines-read/gruntwork-io", "fallback_env": "PIPELINES_GRUNTWORK_READ_TOKEN"}, | ||
| {"name": "customer_org_read", "path": "pipelines-read/${{ github.repository_owner }}", "fallback_env": "PIPELINES_CUSTOMER_ORG_READ_TOKEN"}, | ||
| {"name": "propose_infra_change", "path": "propose-infra-change/${{ github.repository_owner }}", "fallback_env": "PR_CREATE_TOKEN"} | ||
| ] | ||
| api_base_url: ${{ inputs.api_base_url }} | ||
| - name: Checkout Pipelines Actions | ||
| id: checkout_actions | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: pipelines-actions | ||
| repository: ${{ env.PIPELINES_ACTIONS_REPO }} | ||
| ref: ${{ env.PIPELINES_ACTIONS_REF }} | ||
| token: ${{ env.PIPELINES_ACTIONS_REPO == 'gruntwork-io/pipelines-actions' && fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read || fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: Report error if token with access to gruntwork repos is invalid | ||
| if: always() && steps.pipelines-tokens.conclusion == 'success' && steps.checkout_actions.conclusion != 'success' | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| GITHUB_ORG: ${{ github.repository }} | ||
| PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | ||
| shell: bash | ||
| run: | | ||
| logs_url="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" | ||
| msg=$(printf "<h2>❌ Plan for $PR_HEAD_SHA</h2>❌ Gruntwork Pipelines was unable to checkout the <code>pipelines-actions</code> repository. Please ensure the <code>PIPELINES_READ_TOKEN</code> is valid and unexpired. <a href=\"https://docs.gruntwork.io/pipelines/security/machine-users#ci-read-only-user\">Learn More</a><br><br><br><a href=\"$logs_url\">View full logs</a>") | ||
| echo "::error:: $msg" # Results in step failure but rest of logic in this step will still run | ||
| echo "$msg" >> "$GITHUB_STEP_SUMMARY" | ||
| pull_number=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH") | ||
| gh pr comment $pull_number -b "$msg" -R $GITHUB_ORG || true # || true incase this fails on a non-PR run | ||
| - name: Check out repo code | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: infra-live-repo | ||
| fetch-depth: 0 | ||
| token: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: Install Pipelines CLI | ||
| uses: ./pipelines-actions/.github/actions/pipelines-install | ||
| with: | ||
| version: ${{ env.PIPELINES_CLI_VERSION }} | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read }} | ||
| - name: Preflight Checks | ||
| uses: ./pipelines-actions/.github/actions/pipelines-preflight-action | ||
| with: | ||
| PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read }} | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| PR_COMMENT_WRITE_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).propose_infra_change }} | ||
| - name: Pipelines Orchestrate | ||
| id: orchestrate | ||
| uses: ./pipelines-actions/.github/actions/pipelines-orchestrate | ||
| with: | ||
| API_BASE_URL: ${{ inputs.api_base_url }} | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read }} | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| PR_COMMENT_WRITE_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).propose_infra_change }} | ||
| outputs: | ||
| pipelines_jobs: ${{ steps.orchestrate.outputs.jobs }} | ||
| pipelines_execute: | ||
| env: | ||
| JOB_NAME: ${{ contains(matrix.jobs.Action.Command, 'plan') && 'Plan' || 'Apply' }} - ${{ matrix.jobs.ChangeType }} - ${{ matrix.jobs.WorkingDirectory }} | ||
| name: ${{ contains(matrix.jobs.Action.Command, 'plan') && 'Plan' || 'Apply' }} - ${{ matrix.jobs.ChangeType }} - ${{ matrix.jobs.WorkingDirectory }} | ||
| needs: [pipelines_orchestrate] | ||
| runs-on: ${{ fromJSON(inputs.runner) }} | ||
| # GHA can't check for length, so we just check if there is an item in the 0 index | ||
| if: fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0] != null | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| jobs: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs) }} | ||
| steps: | ||
| - name: Record workflow env vars | ||
| env: | ||
| PIPELINES_BINARY_URL: ${{ inputs.pipelines_binary_url }} | ||
| run: | | ||
| time_now=$(date -u +"%s") | ||
| echo "PIPELINES_JOB_START_TIME=$time_now" >> $GITHUB_ENV | ||
| echo "PIPELINES_BINARY_URL=$PIPELINES_BINARY_URL" >> $GITHUB_ENV | ||
| - name: Checkout Pipelines Credentials | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: pipelines-credentials | ||
| repository: ${{ env.PIPELINES_CREDENTIALS_REPO }} | ||
| ref: ${{ env.PIPELINES_CREDENTIALS_REF }} | ||
| - name: Fetch Pipeline Tokens | ||
| id: pipelines-tokens | ||
| uses: ./pipelines-credentials | ||
| env: | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ secrets.PIPELINES_GRUNTWORK_READ_TOKEN || secrets.PIPELINES_READ_TOKEN }} | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: ${{ secrets.PIPELINES_CUSTOMER_ORG_READ_TOKEN || secrets.PIPELINES_READ_TOKEN }} | ||
| PR_CREATE_TOKEN: ${{ secrets.PR_CREATE_TOKEN }} | ||
| with: | ||
| token_requests: | | ||
| [ | ||
| {"name": "gruntwork_read", "path": "pipelines-read/gruntwork-io", "fallback_env": "PIPELINES_GRUNTWORK_READ_TOKEN"}, | ||
| {"name": "customer_org_read", "path": "pipelines-read/${{ github.repository_owner }}", "fallback_env": "PIPELINES_CUSTOMER_ORG_READ_TOKEN"}, | ||
| {"name": "propose_infra_change", "path": "propose-infra-change/${{ github.repository_owner }}", "fallback_env": "PR_CREATE_TOKEN"} | ||
| ] | ||
| api_base_url: ${{ inputs.api_base_url }} | ||
| - name: Checkout Pipelines Actions | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: pipelines-actions | ||
| repository: ${{ env.PIPELINES_ACTIONS_REPO }} | ||
| ref: ${{ env.PIPELINES_ACTIONS_REF }} | ||
| token: ${{ env.PIPELINES_ACTIONS_REPO == 'gruntwork-io/pipelines-actions' && fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read || fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: Check out repo code | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: infra-live-repo | ||
| fetch-depth: 0 | ||
| token: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: Install Pipelines CLI | ||
| uses: ./pipelines-actions/.github/actions/pipelines-install | ||
| with: | ||
| version: ${{ env.PIPELINES_CLI_VERSION }} | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read }} | ||
| - name: Install Mise | ||
| id: mise-toml | ||
| uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 | ||
| with: | ||
| install: true | ||
| cache: true | ||
| version: 2026.4.11 | ||
| working_directory: "./infra-live-repo" | ||
| - name: Configure code auth | ||
| uses: ./pipelines-actions/.github/actions/pipelines-code-auth | ||
| with: | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read }} | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: "[TerragruntExecute]: Run terragrunt ${{ matrix.jobs.Action.Command }} in ${{ matrix.jobs.WorkingDirectory }}" | ||
| id: terragrunt | ||
| uses: ./pipelines-actions/.github/actions/pipelines-execute | ||
| with: | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read }} | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| working_directory: ${{ matrix.jobs.WorkingDirectory }} | ||
| terragrunt_command: ${{ matrix.jobs.Action.Command }} ${{ matrix.jobs.Action.Args }} | ||
| ref: ${{ matrix.jobs.Ref }} | ||
| stack_paths: ${{ toJson(matrix.jobs.StackPaths) }} | ||
| - name: Update comment | ||
| if: always() | ||
| uses: ./pipelines-actions/.github/actions/pipelines-comment-job-update | ||
| with: | ||
| PR_COMMENT_WRITE_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).propose_infra_change }} | ||
| step_name: ${{ matrix.jobs.ChangeType }} | ||
| step_working_directory: ${{ matrix.jobs.WorkingDirectory }} | ||
| step_terragrunt_command: ${{ matrix.jobs.Action.Command }} ${{ matrix.jobs.Action.Args }} | ||
| step_status: ${{ steps.terragrunt.conclusion == 'success' && 'success' || 'failure' }} | ||
| plan_apply_log_file_path: ${{ steps.terragrunt.outputs.plan_folder }} | ||
| extended_log_file_path: ${{ steps.terragrunt.outputs.execute_stdout_log }} | ||
| pipelines_report_file_path: ${{ steps.terragrunt.outputs.pipelines_report_file_path }} | ||
| job_name: ${{ env.JOB_NAME }} | ||
| outputs: | ||
| account_id: ${{ matrix.jobs.AccountId }} | ||
| branch: ${{ matrix.jobs.Ref }} | ||
| working_directory: ${{ matrix.jobs.WorkingDirectory }} | ||
| terragrunt_command: ${{ matrix.jobs.Action.Command }} ${{ matrix.jobs.Action.Args }} | ||
| additional_data: ${{ toJson(matrix.jobs.AdditionalData) }} | ||
| child_account_id: ${{ matrix.jobs.AdditionalData.ChildAccountId }} | ||
| pipelines_status_check: | ||
| name: "Pipelines Status Check" | ||
| runs-on: ${{ fromJSON(inputs.runner) }} | ||
| needs: [pipelines_orchestrate, pipelines_execute] | ||
| if: always() | ||
| steps: | ||
| - name: Record workflow env vars | ||
| env: | ||
| PIPELINES_BINARY_URL: ${{ inputs.pipelines_binary_url }} | ||
| run: | | ||
| time_now=$(date -u +"%s") | ||
| echo "PIPELINES_JOB_START_TIME=$time_now" >> $GITHUB_ENV | ||
| echo "PIPELINES_BINARY_URL=$PIPELINES_BINARY_URL" >> $GITHUB_ENV | ||
| - name: Checkout Pipelines Credentials | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: pipelines-credentials | ||
| repository: ${{ env.PIPELINES_CREDENTIALS_REPO }} | ||
| ref: ${{ env.PIPELINES_CREDENTIALS_REF }} | ||
| - name: Fetch Pipeline Tokens | ||
| id: pipelines-tokens | ||
| uses: ./pipelines-credentials | ||
| env: | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ secrets.PIPELINES_GRUNTWORK_READ_TOKEN || secrets.PIPELINES_READ_TOKEN }} | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: ${{ secrets.PIPELINES_CUSTOMER_ORG_READ_TOKEN || secrets.PIPELINES_READ_TOKEN }} | ||
| PR_CREATE_TOKEN: ${{ secrets.PR_CREATE_TOKEN }} | ||
| with: | ||
| token_requests: | | ||
| [ | ||
| {"name": "gruntwork_read", "path": "pipelines-read/gruntwork-io", "fallback_env": "PIPELINES_GRUNTWORK_READ_TOKEN"}, | ||
| {"name": "customer_org_read", "path": "pipelines-read/${{ github.repository_owner }}", "fallback_env": "PIPELINES_CUSTOMER_ORG_READ_TOKEN"}, | ||
| {"name": "propose_infra_change", "path": "propose-infra-change/${{ github.repository_owner }}", "fallback_env": "PR_CREATE_TOKEN"} | ||
| ] | ||
| api_base_url: ${{ inputs.api_base_url }} | ||
| - name: Checkout Pipelines Actions | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: pipelines-actions | ||
| repository: ${{ env.PIPELINES_ACTIONS_REPO }} | ||
| ref: ${{ env.PIPELINES_ACTIONS_REF }} | ||
| token: ${{ env.PIPELINES_ACTIONS_REPO == 'gruntwork-io/pipelines-actions' && fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read || fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: Check out repo code | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
| with: | ||
| path: infra-live-repo | ||
| token: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: Install Pipelines CLI | ||
| uses: ./pipelines-actions/.github/actions/pipelines-install | ||
| with: | ||
| version: ${{ env.PIPELINES_CLI_VERSION }} | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read }} | ||
| - name: Configure code auth | ||
| uses: ./pipelines-actions/.github/actions/pipelines-code-auth | ||
| with: | ||
| PIPELINES_GRUNTWORK_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).gruntwork_read }} | ||
| PIPELINES_CUSTOMER_ORG_READ_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: Pipelines After Hooks | ||
| id: hooks_after | ||
| uses: ./pipelines-actions/.github/actions/pipelines-hooks-after | ||
| with: | ||
| GH_ARTIFACT_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).customer_org_read }} | ||
| - name: Check Status | ||
| if: always() | ||
| shell: bash | ||
| working-directory: ./infra-live-repo | ||
| env: | ||
| API_BASE_URL: ${{ inputs.api_base_url }} | ||
| GH_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).propose_infra_change }} | ||
| GH_ARTIFACT_TOKEN: ${{ fromJson(steps.pipelines-tokens.outputs.tokens_json).propose_infra_change }} | ||
| JOB_FAILED: ${{ failure() }} | ||
| run: | | ||
| pipelines status-update finalize \ | ||
| --working-directory . \ | ||
| --ci github-actions >/tmp/finalize-output.json | ||
| pipelines inventory usage --working-directory . || true | ||
| pipeline_status=$(jq -r '.status' </tmp/finalize-output.json) | ||
| cat /tmp/finalize-output.json | ||
| printf '%.s─' $(seq 1 "$(tput -T dumb cols)") | ||
| echo " " | ||
| if [[ "$JOB_FAILED" != "true" && "$pipeline_status" == "success" ]]; then | ||
| echo -e "✅ \033[1;32mPipeline Passed\033[0m" | ||
| exit 0 | ||
| else | ||
| echo -e "❌ \033[1;31mPipeline Failed\033[0m" | ||
| exit 1 | ||
| fi | ||