Skip to content

tf-plan: report one check run for the whole workflow - #79

Draft
dev-milos wants to merge 1 commit into
mainfrom
feature/plan-check-run-bracket
Draft

tf-plan: report one check run for the whole workflow#79
dev-milos wants to merge 1 commit into
mainfrom
feature/plan-check-run-bracket

Conversation

@dev-milos

@dev-milos dev-milos commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Problem

The Terraform plan check run was created and concluded inside the terraform-plan job, which only runs once validate has passed. Three things follow:

  1. A failed validate produced no check at all. terraform-plan has needs: validate and no if:, so both Checks API calls were skipped and the head commit was left with nothing. A pull request gated on that check waits forever on something that never arrives. Reproduced in the test org — run 33066980306: validate failed, terraform-plan skipped, zero check runs on the SHA.
  2. Every run left a dangling check run. The terminal call was a second checks.create rather than an update — the in-progress run's id was never captured — so each SHA ended up with a completed check run and one stuck at in_progress forever.
  3. A cancelled or crashed job stranded the check, because nothing outside that job could conclude it.

Change

validate and terraform-plan become one validate-and-plan job that opens the check run as its first step and reports the outcome as its last:

Generate a token
Open check run              <- in_progress, before any work
Checkout GCSS
Checkout config repo
Validate repos/*.yaml
Validate organisation/*.yaml
GCSS config setup
Setup terraform and run plan
Report result               <- if: always()

Skipping the plan on invalid config is what needs: bought us, and steps already do that on their own — everything after a failed step is skipped. In exchange the check-run lifecycle is a straight line through a single job: job.status and the step outcomes replace all the cross-job plumbing (no job outputs, no needs, no second token), and the run drops three runner allocations and a duplicate Checkout GCSS. On the test org that is 99s → 76s for the happy path.

Report result derives the conclusion in one place: cancelled first, then a validation step failure, then anything else that failed, then the existing plan summary on success. It updates the run opened at the top by id, falling back to create when there is none (the opening step is continue-on-error, so a Checks API blip cannot block the plan).

The check keeps the name Terraform plan — consumers list it as a required status check — and now carries what happened in its title: Configuration Validation Failed, Terraform Plan Failed, Terraform Plan Cancelled. skipped is deliberately never used as a conclusion, since GitHub counts it as passing a required check.

The PR comment is posted after the check is resolved and is truncated at 65000 chars. issues.createComment rejects bodies over 65536, and an unhandled throw there must not be able to strand the merge gate.

No consumer change is required — inputs, secrets, and the check name are untouched. The only visible difference is one job in the Actions UI instead of two.

Verification

End-to-end in milos-org/github-configuration, with main pointed at this branch.

Case Run Result
Valid config 33177432800 one check run, in_progresssuccess with the plan summary, same id throughout; comment posted
Invalid YAML 33177435118 validation step failed, everything after it skipped, Report result still ran → failure / Configuration Validation Failed. Previously: no check at all
Cancelled mid-plan 33177587865 Setup terraform and run plan: cancelled, Report result: success → check cancelled, not stranded at in_progress

Check runs per SHA, filter=all — before this change vs after:

before  2  id=98499742611 completed/success   id=98499609070 in_progress/-   <- stuck forever
after   1  id=98869630461 completed/success

Also checked before pushing: actionlint clean, both github-script bodies pass node --check, and Report result was executed against 13 stubbed environments — including cancellation during validation (must report cancelled, not a validation failure), a failure before validation ran, the create fallback, a fork PR with no PR number, and a ~4000-resource plan that truncates both the check text and the comment.

@dev-milos dev-milos changed the title tf-plan: bracket the whole workflow with one check run tf-plan: report one check run for the whole workflow Aug 28, 2026
The `Terraform plan` check run was created and concluded inside the
`terraform-plan` job, which only runs once `validate` has passed. A
failed validation therefore skipped both calls and left the head commit
with no check at all - a pull request gated on that check waited forever
on something that never arrived. The terminal call was also a second
`checks.create` rather than an update, so every run left a dangling
`in_progress` check beside the completed one, and nothing outside the job
could conclude the check when a run was cancelled.

Merge `validate` and `terraform-plan` into one job that opens the check
run as its first step and reports the outcome as its last, `if: always()`.
Skipping the plan on invalid config is what `needs:` bought us, and steps
already do that on their own; in exchange the check-run lifecycle is a
straight line through a single job, `job.status` and the step outcomes
replace the cross-job plumbing, and the run drops three runner
allocations and a duplicate checkout.

The check keeps the name `Terraform plan` - consumers list it as a
required status check - and now carries what happened in its title:
validation failed, the plan failed, or the run was cancelled. `skipped`
is deliberately never used as a conclusion, since GitHub counts it as
passing a required check.

The plan comment is posted after the check is resolved and is truncated,
so an over-long body cannot strand the merge gate.
@dev-milos
dev-milos force-pushed the feature/plan-check-run-bracket branch from 05bca1c to 56a6edd Compare August 28, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant