name: Terraform on: push: branches: [main] paths: - "terraform/**" - ".github/workflows/terraform.yml" # Requires these repository secrets: # AGE_SECRET_KEY — age private key for SOPS decryption # Serialize Terraform runs so two merges can't apply against the state # concurrently. Never cancel an in-flight run (an interrupted apply can # corrupt state) — queue instead. concurrency: group: terraform-state cancel-in-progress: false jobs: plan: name: Plan runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tofu - name: Decrypt secrets uses: ./.github/actions/sops-decrypt with: age-key: ${{ secrets.AGE_SECRET_KEY }} - name: Set backend credentials uses: ./.github/actions/tofu-backend-creds - name: tofu init working-directory: terraform/ run: tofu init - name: tofu plan working-directory: terraform/ run: tofu plan -out=tfplan - name: Upload plan if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: actions/upload-artifact@v7 with: name: tfplan path: terraform/tfplan retention-days: 1 apply: name: Apply needs: plan if: github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: production steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-tofu - name: Decrypt secrets uses: ./.github/actions/sops-decrypt with: age-key: ${{ secrets.AGE_SECRET_KEY }} - name: Set backend credentials uses: ./.github/actions/tofu-backend-creds - name: tofu init working-directory: terraform/ run: tofu init - name: Download plan uses: actions/download-artifact@v8 with: name: tfplan path: terraform/ - name: tofu apply working-directory: terraform/ run: tofu apply -auto-approve tfplan