LeanerCloud/cloud-commitments-cli#1803 serializes every writer of the Azure Terraform state blob on a shared concurrency group, azure-tfstate-<environment>. The group is a hand-written literal in three places:
.github/workflows/deploy-azure.yml (build-and-deploy)
.github/workflows/cleanup-staging.yml (destroy-azure)
.github/workflows/rollback.yml (rollback-azure)
Nothing enforces that they stay in sync. A new workflow that writes github-*.terraform.tfstate without the group, or a typo in the prefix in any one of the three, silently does not serialize. That is the same class of silent failure as the original bug in LeanerCloud/cloud-commitments-cli#1801: nothing is red, nothing is logged, and the damage is only visible later as missing state entries.
This is the residual risk LeanerCloud/cloud-commitments-cli#1803 explicitly named rather than solved, filed so it does not evaporate.
Suggested guard
A CI check asserting that every job which writes a Terraform state key also carries a concurrency group derived from that key. Roughly:
- Parse
.github/workflows/*.yml.
- For each job, find any
key = "..." / backend-config construction or terraform apply|destroy against a known state.
- Assert the job has a
concurrency.group, and that the group's environment suffix is derived from the same expression as the state key.
- Fail with the offending
file:job when they disagree.
Even a narrow version limited to the Azure blob would catch the realistic regression, which is someone adding a fourth writer. Worth extending to AWS and GCP once LeanerCloud/cloud-commitments-cli#1806 lands, since the same shared-group convention will apply there.
Alternative worth weighing first: if the group could be derived rather than repeated, the drift becomes unrepresentable and no guard is needed. GitHub expressions cannot call a shared function, so this probably means a composite action or a generated workflow, which may cost more than the guard.
LeanerCloud/cloud-commitments-cli#1803 serializes every writer of the Azure Terraform state blob on a shared concurrency group,
azure-tfstate-<environment>. The group is a hand-written literal in three places:.github/workflows/deploy-azure.yml(build-and-deploy).github/workflows/cleanup-staging.yml(destroy-azure).github/workflows/rollback.yml(rollback-azure)Nothing enforces that they stay in sync. A new workflow that writes
github-*.terraform.tfstatewithout the group, or a typo in the prefix in any one of the three, silently does not serialize. That is the same class of silent failure as the original bug in LeanerCloud/cloud-commitments-cli#1801: nothing is red, nothing is logged, and the damage is only visible later as missing state entries.This is the residual risk LeanerCloud/cloud-commitments-cli#1803 explicitly named rather than solved, filed so it does not evaporate.
Suggested guard
A CI check asserting that every job which writes a Terraform state key also carries a concurrency group derived from that key. Roughly:
.github/workflows/*.yml.key = "..."/ backend-config construction orterraform apply|destroyagainst a known state.concurrency.group, and that the group's environment suffix is derived from the same expression as the state key.file:jobwhen they disagree.Even a narrow version limited to the Azure blob would catch the realistic regression, which is someone adding a fourth writer. Worth extending to AWS and GCP once LeanerCloud/cloud-commitments-cli#1806 lands, since the same shared-group convention will apply there.
Alternative worth weighing first: if the group could be derived rather than repeated, the drift becomes unrepresentable and no guard is needed. GitHub expressions cannot call a shared function, so this probably means a composite action or a generated workflow, which may cost more than the guard.