From 73926a961c2ab933b55eed951aef55e07412505e Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 24 Aug 2026 00:54:08 -0700 Subject: [PATCH] fix(ci): give push builds a base their audits can actually read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Push builds fail the migration audit: ✗ Migration safety check could not run. Cannot diff against 'HEAD~1'. `actions/checkout` sets no `fetch-depth`, so it defaults to 1 — a single-commit clone in which `HEAD~1` does not resolve. Both diff-based audits named `HEAD~1` as their push base, so neither has ever had a base to read. The migration audit answered that with `✓ No new migrations to check` and exit 0, so it had never run on a push build at all; #7022 made it say it could not run instead, which is what surfaced this. The block-registry check reports `⚠ … skipping` on the same input — visible, and equally never run. `HEAD~1` was the wrong base regardless. It names the last commit, so a push carrying several commits audits the tip and lets every earlier commit through: 3-commit push, HEAD~1 base: mig3.sql 3-commit push, before base: mig1.sql mig2.sql mig3.sql The base is now `github.event.before` — the tip the branch had before the push, which is what GitHub provides for exactly this. It is fetched by SHA at depth 1; the audits diff two tips and need no common ancestry between them. Resolved once in a step both audits read, so the two cannot drift apart. `HEAD~1` survives only as the fallback for an all-zero `before` (a new branch, with no predecessor to diff), which is what `fetch-depth: 2` now covers. Verified: both audits accept a raw SHA base and pass; the multi-commit case above is a real reproduction, not a description. --- .github/workflows/test-build.yml | 48 ++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 181f2f9b8df..690905622d2 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -14,8 +14,18 @@ jobs: timeout-minutes: 15 steps: + # The diff-based audits below need a base commit to read, and the default + # depth of 1 clones a single commit with no parent. They normally fetch + # their base by SHA (see "Resolve base ref"), so this depth only covers the + # `HEAD~1` fallback — but without it that fallback resolves to nothing. + # + # Worth stating because the failure was invisible for so long: the migration + # audit read the resulting `git diff` failure as "no migrations changed" and + # exited 0, so it had never actually run on a push build. - name: Checkout code uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 2 - name: Setup Bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 @@ -112,18 +122,32 @@ jobs: # # Depth stays at 1 — without a merge-base the migration audit diffs the two # tips, which under `--diff-filter=AM` is exactly the migrations new here. - - name: Fetch base ref for diff-based audits - if: github.event_name == 'pull_request' - run: git fetch --depth=1 origin "${{ github.base_ref }}" - - - name: Check block registry invariants + # Resolved once for both diff-based audits, and never with `|| true`: a + # swallowed fetch leaves the base absent, which neither audit can tell apart + # from a branch that changed nothing. + # + # On push the base is `github.event.before`, the tip the branch had before + # this push — not `HEAD~1`, which names only the last commit and would let a + # multi-commit push slip every earlier commit's migrations past the audit. + # It is fetched by SHA at depth 1; the audits diff two tips and need no + # common ancestry. An all-zero `before` means the branch is new and has no + # predecessor to diff, so `HEAD~1` remains the fallback there. + - name: Resolve base ref for diff-based audits + id: audit_base run: | if [ "${{ github.event_name }}" = "pull_request" ]; then - BASE_REF="origin/${{ github.base_ref }}" + git fetch --depth=1 origin "${{ github.base_ref }}" + echo "ref=origin/${{ github.base_ref }}" >> "$GITHUB_OUTPUT" + elif [ -n "${{ github.event.before }}" ] && + [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then + git fetch --depth=1 origin "${{ github.event.before }}" + echo "ref=${{ github.event.before }}" >> "$GITHUB_OUTPUT" else - BASE_REF="HEAD~1" + echo "ref=HEAD~1" >> "$GITHUB_OUTPUT" fi - bun run apps/sim/scripts/check-block-registry.ts "$BASE_REF" + + - name: Check block registry invariants + run: bun run apps/sim/scripts/check-block-registry.ts "${{ steps.audit_base.outputs.ref }}" - name: Lint code run: bun run lint:check @@ -138,13 +162,7 @@ jobs: run: bun run docs-manifest:check - name: Migration safety (zero-downtime) audit - run: | - if [ "${{ github.event_name }}" = "pull_request" ]; then - BASE_REF="origin/${{ github.base_ref }}" - else - BASE_REF="HEAD~1" - fi - bun run check:migrations "$BASE_REF" + run: bun run check:migrations "${{ steps.audit_base.outputs.ref }}" # Every workspace, not just realtime. packages/emcn, packages/utils, # apps/desktop and apps/docs had no type check in CI at all; apps/sim's