Jump to content
Toggle menu
  • 22 articles
  • 1 files
  • 1 users
  • 216 edits
terraform-github-tacos
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Apply-before-merge

From terraform-github-tacos
More actions

With apply-before-merge, the pull request's own run waits for the approval, applies, and merges. The default branch then holds only what was applied, and a pull request that does not merge after its apply leaves production ahead of it, so the merge job fails. This is the order Atlantis and digger use, and it is usually called apply-before-merge.

Other names


A pull request behind its base may be planned and applied unless require-up-to-date is "true". The plan runs on the merge commit, so a change on the base to the same workspace makes the fresh plan differ and stops the apply, and a change to another workspace does not.

name: tofu
on:
  pull_request:
  workflow_dispatch:
    inputs:
      workspace:
        description: The workspace to apply from this branch, once its environment approves.
        required: true
        type: choice
        options: &workspaces [network, dns]
permissions: {}
jobs:
  plan:
    name: ${{ matrix.workspace }} plan
    if: github.event_name == 'pull_request'
    strategy:
      fail-fast: false
      matrix:
        workspace: *workspaces
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: femiwiki/terraform-github-tacos@v0.7.0
        with:
          step: plan
          workspace: ${{ matrix.workspace }}

      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0  # v7.0.0
        with:
          persist-credentials: false

      - uses: dflook/tofu-plan@c7416238983bdd94c5486fb856bb7358f2bcc935  # v3.1.0
        id: plan
        env:
          GITHUB_TOKEN: ${{ github.token }}
        with:
          path: ${{ matrix.workspace }}

      - uses: femiwiki/terraform-github-tacos@v0.7.0
        with:
          step: changes
          workspace: ${{ matrix.workspace }}
          changes: ${{ steps.plan.outputs.changes }}
          plan-json: ${{ steps.plan.outputs.json_plan_path }}
          plan-text: ${{ steps.plan.outputs.text_plan_path }}

      - uses: femiwiki/terraform-github-tacos@v0.7.0
        if: always()
        with:
          step: collapse

  pending:
    needs: plan
    if: >-
      !cancelled() && (github.event_name == 'workflow_dispatch'
      || (needs.plan.result == 'success' && github.event.pull_request.head.repo.full_name == github.repository))
    runs-on: ubuntu-latest
    permissions:
      actions: write
      checks: read
      pull-requests: read
    outputs:
      workspaces: ${{ steps.pending.outputs.workspaces }}
    steps:
      - uses: femiwiki/terraform-github-tacos@v0.7.0
        id: pending
        with:
          step: pending
          apply-before-merge: "true"
          workspaces: ${{ inputs.workspace && format('["{0}"]', inputs.workspace) || '' }}

  apply:
    name: ${{ matrix.workspace }} apply
    needs: pending
    if: needs.pending.outputs.workspaces != '[]'
    strategy:
      fail-fast: false
      matrix:
        workspace: ${{ fromJSON(needs.pending.outputs.workspaces) }}
    runs-on: ubuntu-latest
    environment: ${{ matrix.workspace }}
    permissions:
      actions: read
      contents: read
      pull-requests: write
    steps:
      - uses: femiwiki/terraform-github-tacos@v0.7.0
        id: approver
        with:
          step: apply
          workspace: ${{ matrix.workspace }}
          apply-before-merge: "true"

      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0  # v7.0.0
        with:
          persist-credentials: false

      - uses: dflook/tofu-apply@e3e5dfc8f935d4b152c767801046f63abf8a77d3  # v3.1.0
        id: apply
        env:
          GITHUB_TOKEN: ${{ github.token }}
        with:
          path: ${{ matrix.workspace }}
          auto_approve: ${{ github.event_name == 'workflow_dispatch' }}

      - uses: femiwiki/terraform-github-tacos@v0.7.0
        if: always()
        with:
          step: result
          workspace: ${{ matrix.workspace }}
          outcome: ${{ steps.apply.outcome }}
          plan-json: ${{ steps.apply.outputs.json_plan_path }}
          plan-text: ${{ steps.apply.outputs.text_plan_path }}
          approver: ${{ steps.approver.outputs.approver }}

  # Make this the one required check
  gate:
    name: ${{ github.event_name == 'pull_request' && 'tofu gate' || 'tofu gate (pull requests only)' }}
    needs: [plan, pending, apply]
    if: always() && github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    permissions: {}
    steps:
      - uses: femiwiki/terraform-github-tacos@v0.7.0
        with:
          step: gate
          needs: ${{ toJSON(needs) }}

  merge:
    needs: [pending, apply, gate]
    if: >-
      !cancelled() && needs.gate.result == 'success'
      && (needs.apply.result == 'success' || needs.pending.outputs.workspaces == '[]')
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: write
      deployments: read
      pull-requests: write
    steps:
      - uses: femiwiki/terraform-github-tacos@v0.7.0
        with:
          step: merge
          apply-before-merge: "true"

Stacked pull requests

In a native stack, which GitHub has in public preview, approving a pull request does what merging it does: the bottom one applies and merges alone, and one higher up applies its own plan, which carries every pull request below it, and merges the whole stack. Higher up, pending fails when that plan changes more than one workspace, since their applies run side by side and one may need what another has yet to apply. Apply such a stack from the bottom: once the lowest pull request merges, GitHub retargets the next one at the default branch, and its next run plans it from there.

A pull request that targets another pull request's branch without being in a native stack is never applied.