ci(terraform-isolation): re-evaluate the gate on label changes

This commit is contained in:
Adam Moussa 2026-09-10 20:11:20 -04:00
parent 763b82a77d
commit a95c11527c
No known key found for this signature in database
5 changed files with 61 additions and 11 deletions

View file

@ -3,6 +3,16 @@ name: Frontend checks
on:
pull_request:
branches: [main, dev, staging]
# labeled/unlabeled re-evaluate terraform-isolation when the override
# label is added or removed. Without them a mixed PR can keep a stale
# green isolation check after the label is taken off. Other jobs skip
# on those events so a label change does not re-run the suite.
types:
- opened
- synchronize
- reopened
- labeled
- unlabeled
push:
branches: [main, dev, staging]
workflow_dispatch: {}
@ -13,12 +23,15 @@ permissions:
jobs:
build-and-test:
name: Build and test
# Skipped jobs satisfy the required checks on the unchanged SHA.
if: github.event_name != 'pull_request' || (github.event.action != 'labeled' && github.event.action != 'unlabeled')
# Org reusable workflow (Node 24): format check, lint, build, unit tests.
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-frontend.yaml@af0f002e14a08cdbfd879c1183bfe7eb2604bce9 # v1.0.8
with:
node-version: "24"
governance:
if: github.event_name != 'pull_request' || (github.event.action != 'labeled' && github.event.action != 'unlabeled')
# Repo-owned guarantee that every frontend quality gate runs from this
# repository, independent of (and in addition to) the reusable workflow.
# `npm run verify` is the single command that chains: format check, lint
@ -76,8 +89,9 @@ jobs:
# application code (scripts/check-terraform-isolation.mjs). A merge that
# does both queues an HCP VCS run and a content release at the same time,
# and the two race for the workspace lock. The
# `terraform-isolation-override` label is the reviewed exception; it is
# read when the job runs, so re-run this workflow after labeling.
# `terraform-isolation-override` label is the reviewed exception. This
# job runs on labeled/unlabeled as well as the default PR types so the
# check matches the labels currently on the PR.
name: Terraform and application changes are isolated
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
@ -97,6 +111,7 @@ jobs:
visual-regression:
name: Visual regression
if: github.event_name != 'pull_request' || (github.event.action != 'labeled' && github.event.action != 'unlabeled')
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.61.1-noble
steps:

View file

@ -49,10 +49,13 @@ and synthesis. A task is not done until this is green.
against synthetic plan JSON. Real import and controlled-update plans from HCP
are migration evidence reviewed by a human before an approved apply
(`terraform/README.md`).
- **The isolation gate reads the override when it runs** — the
- **The isolation gate re-evaluates on label changes** — the
`terraform-isolation-override` label is the only way to merge a mixed
Terraform/application PR, the gate logs the override on the run, and the
workflow must be re-run after the label is added or removed.
Terraform/application PR. `ci.yaml` runs `terraform-isolation` on
`labeled` and `unlabeled` as well as the default pull-request types, so
adding or removing the label re-checks the current labels. The other CI
jobs skip on those events. Removing the label fails a mixed PR that had
previously passed with the override.
## Where the gates run

View file

@ -17,7 +17,9 @@
// TERRAFORM_ISOLATION_OVERRIDE=true downgrades a failure to a warning. CI sets
// it only when the PR carries the `terraform-isolation-override` label, which
// reviewers grant to the rare change that must introduce Terraform variables
// together with the workflow that consumes them.
// together with the workflow that consumes them. The checker has no memory of
// a previous pass: the same mixed diff fails again as soon as the override
// env is unset (label removal).
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
import path from "node:path";

View file

@ -109,6 +109,33 @@ test("CLI override downgrades a mixed change to a warning that names the label",
assert.equal(notTrue.status, 1);
});
test("removing the override fails a mixed change that was previously green", () => {
const files = ["terraform/live/dev/main.tf", ".github/workflows/deploy.yml"];
const previouslyGreen = runGate(files, {
TERRAFORM_ISOLATION_OVERRIDE: "true",
});
assert.equal(
previouslyGreen.status,
0,
previouslyGreen.stdout + previouslyGreen.stderr,
);
assert.match(previouslyGreen.stdout, /WARNING/);
// CI sets TERRAFORM_ISOLATION_OVERRIDE from contains(...labels), which is
// the string "false" after the label is removed. A stale green check must
// not survive that.
const afterLabelRemoved = runGate(files, {
TERRAFORM_ISOLATION_OVERRIDE: "false",
});
assert.equal(
afterLabelRemoved.status,
1,
afterLabelRemoved.stdout + afterLabelRemoved.stderr,
);
assert.match(afterLabelRemoved.stdout, /FAIL/);
assert.match(afterLabelRemoved.stdout, /deploy\.yml/);
});
test("CLI refuses to run without a base ref or --stdin", () => {
const result = spawnSync(process.execPath, [SCRIPT], { encoding: "utf8" });
assert.notEqual(result.status, 0);

View file

@ -221,11 +221,14 @@ the labels. Push-to-`dev` releases return behind the repository variable
`.github/workflows/ci.yaml` enforces this; documentation and the
`scripts/*terraform*` tooling are allowed alongside. A reviewer may add the
`terraform-isolation-override` label for the rare change that must introduce
Terraform variables together with the workflow that consumes them (PR A and
PR C), then re-run the workflow so the job reads the label. The label is the
approval record. The override is temporary: a follow-up PR after PR C
removes the label path from the checker and workflow so the gate has no
exception.
Terraform variables together with the workflow that consumes them (PR C).
Adding or removing that label re-runs `terraform-isolation` against the
labels currently on the PR; the other CI jobs skip on those events so a
label change does not re-run the suite. Removing the label fails a mixed
PR that had previously passed with the override, so a stale green check
cannot merge. The label is the approval record. The override is temporary:
a follow-up PR after PR C removes the label path from the checker and
workflow so the gate has no exception.
- **Every Terraform merge produces a VCS run.** A human confirms or discards
it before the next content release. Do not leave a pending run on the
workspace.