"Not applying the plan - it has changed from the plan on the PR"
Problem description
I'm using tofu-plan (on the PR) and tofu-apply (at merge time), and the apply step is failing, seemingly because of whitespace differences. Note that the + is left-justified in the PR and right-justified at execution time.
Here's an excerpt from the error; full logs are included in a Gist below.
Not applying the plan - it has changed from the plan on the PR
The plan on the PR must be up to date. Alternatively, set the auto_approve input to 'true' to apply outdated plans
Performing diff between the pull request plan and the plan generated at execution time.
> are lines from the plan in the pull request
< are lines from the plan generated at execution
Plan differences:
3c3
< + create
---
> + create
Terraform version
OpenTofu v1.10.2
Backend
Google Cloud Storage
Workflow YAML
name: Deployment
on:
workflow_dispatch:
push:
branches: [development, staging, demo, main]
env:
ENVIRONMENT: ${{ github.ref_name == 'main' && 'production' || github.ref_name }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
pull-requests: write
environment: ${{ github.ref_name == 'main' && 'production' || github.ref_name }}
steps:
- uses: actions/checkout@v4
- uses: google-github-actions/auth@v2
id: auth
with:
token_format: access_token
create_credentials_file: true
project_id: ${{ vars.GOOGLE_PROJECT_ID }}
workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ vars.SERVICE_ACCOUNT }}
- uses: dflook/tofu-output@v2-ghcr
id: tf-outputs
with:
path: infrastructure
- uses: dflook/tofu-apply@v2-ghcr
id: apply
with:
path: infrastructure
var_file: infrastructure/environments/${{ env.ENVIRONMENT }}.tfvars
variables: |
image_url = "${{ steps.tf-outputs.outputs.image_url }}"
# NOTE: the use of outputs here seemed necessary to get the image_url, which is the result of a docker build step. Open to suggestions about how this might be better achieved.
Workflow log
https://gist.github.com/jelder/e963133626a01652b2d9c25b10666fa3
Has debug logging been enabled?
- [x] Yes, the
ACTIONS_STEP_DEBUGsecret was set totruewhen capturing the workflow log above. I understand that if I have not done this, I may not receive a response.
Hi @jelder, Can you share the workflow that does the plan as well?
@dflook sure!
name: PR Deployment
on:
pull_request:
types: [opened, synchronize, reopened]
branches: [main, demo, staging, development]
env:
DOCKER_BUILDKIT: 1
REGISTRY: gcr.io
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image_tag: ${{ steps.image_tag.outputs.image_tag }}
steps:
- uses: actions/checkout@v4
- name: Set image_tag
id: image_tag
run: |
repo_name=$(basename $GITHUB_REPOSITORY)
image_tag=${REGISTRY}/${{ vars.GOOGLE_PROJECT_ID }}/${repo_name}:${{ github.sha }}
echo "image_tag=$image_tag" >> "$GITHUB_OUTPUT"
- uses: google-github-actions/auth@v2
id: auth
with:
token_format: access_token
create_credentials_file: true
project_id: ${{ vars.GOOGLE_PROJECT_ID }}
workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ vars.SERVICE_ACCOUNT }}
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: oauth2accesstoken
password: ${{ steps.auth.outputs.access_token }}
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
id: build
with:
context: .
push: true
tags: ${{ steps.image_tag.outputs.image_tag }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
build-args: |
PROJECT_TAG=pr-${{ github.event.number }}
tofu-plan:
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
id-token: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: dflook/tofu-validate@v2-ghcr
with:
path: infrastructure
- name: Set paccurate_env
uses: actions/github-script@v7
with:
script: |
const base_ref = process.env.GITHUB_BASE_REF;
const paccurate_env = (() => {
switch (base_ref) {
case "main":
return "production";
case "development":
case "demo":
case "staging":
return base_ref;
}
})();
console.log({ base_ref, paccurate_env });
if (paccurate_env) {
core.exportVariable('paccurate_env', paccurate_env);
}
- uses: google-github-actions/auth@v2
id: auth
with:
token_format: access_token
create_credentials_file: true
project_id: ${{ vars.GOOGLE_PROJECT_ID }}
workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ vars.SERVICE_ACCOUNT }}
- uses: dflook/tofu-plan@v2-ghcr
id: plan
with:
path: infrastructure
var_file: infrastructure/environments/${{ env.paccurate_env }}.tfvars
variables: |
image_url = "${{ needs.build.outputs.image_tag }}"
Thanks, it looks like there is an issue with the generated diff in the workflow log. This diff should just be informative - I think there is a genuine difference in the plan.
Looking at the workflow log, the PR includes a container image url of ghcr.io/... by that's missing in the plan generated by the apply step. I think the variable may not be set right.
Regarding the container image, I did struggle with that aspect of this architecture. Given the choice between rebuilding the docker image, and deploying exactly the one resulting from the reviewed PR, I'd prefer the latter (deploy the PR one).
My current approach is to use dflook/tofu-output@v2-ghcr to fetch the output from the plan in the PR but that doesn't seem to be working. My guess is it doesn't do the magical "get the plan from a PR comment" thing. Curious if you have any recommendations here?