Split serialization cross-version CI into explicit backward/forward-compat jobs
Some checks are pending
Bulldozer serialization compat / Check if bulldozer serialization changed (push) Waiting to run
Bulldozer serialization compat / Backward compat (new code reads old data) (push) Blocked by required conditions
Bulldozer serialization compat / Forward compat (old code reads new data) (push) Blocked by required conditions
Bulldozer serialization compat / No serialization changes (skipped) (push) Blocked by required conditions
DB migration compat / Check if migrations changed (push) Waiting to run
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Blocked by required conditions
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Blocked by required conditions
DB migration compat / No migration changes (skipped) (push) Blocked by required conditions

Co-Authored-By: Konstantin Wohlwend <n2d4xc@gmail.com>
This commit is contained in:
Devin AI 2026-07-18 20:58:47 +00:00
parent b1be2a0f85
commit f8c890e472

View File

@ -5,7 +5,10 @@ name: Bulldozer serialization compat
# makes an older build unable to read data the new code writes, which matters for rollbacks) would
# corrupt or lose data. This workflow proves both directions by having each code version read a dump
# written by the other. The per-version golden-fixture unit test (serialization-compat.test.ts) is the
# first line of defence; this cross-version job is the belt-and-suspenders check.
# first line of defence; the two cross-version jobs below (backward-compat and forward-compat) are the
# belt-and-suspenders check. They are split into separate jobs on purpose: backward compat (upgrade)
# must always hold, whereas forward compat (rollback) may legitimately break on a non-additive format
# change — keeping them separate makes a forward-compat failure explicit without masking backward compat.
on:
push:
@ -64,8 +67,11 @@ jobs:
echo "changed=true" >> $GITHUB_OUTPUT
fi
cross-version:
name: Cross-version read (base ↔ current)
# Backward compatibility (production UPGRADE): the new code must read data the old code wrote.
# This is the direction that must ALWAYS hold — if it fails, deploying the new code would break
# reads of already-persisted production data.
backward-compat:
name: Backward compat (new code reads old data)
needs: check-changed
if: needs.check-changed.outputs.changed == 'true'
runs-on: ubuntu-latest
@ -115,20 +121,71 @@ jobs:
pnpm install --frozen-lockfile
pnpm exec turbo run build --filter=@hexclave/shared
- name: Emit dump with current code
working-directory: current
run: pnpm -C apps/bulldozer-js exec tsx "$HARNESS_REL" emit "$GITHUB_WORKSPACE/new.json"
- name: Emit dump with base code
working-directory: base
run: pnpm -C apps/bulldozer-js exec tsx "$HARNESS_REL" emit "$GITHUB_WORKSPACE/old.json"
# Backward compatibility (production upgrade): the new code must read what the old code wrote.
- name: Current code reads base-written dump
working-directory: current
run: pnpm -C apps/bulldozer-js exec tsx "$HARNESS_REL" verify "$GITHUB_WORKSPACE/old.json"
# Forward compatibility (production rollback): the old code must read what the new code wrote.
# Forward compatibility (production ROLLBACK): the old code must read data the new code wrote.
# This can legitimately fail when a change makes the new format unreadable by older builds (e.g. a
# non-additive format change). It runs as its OWN job so that such a failure is explicit and
# actionable, and does NOT mask the backward-compat result above.
forward-compat:
name: Forward compat (old code reads new data)
needs: check-changed
if: needs.check-changed.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout current branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
path: current
- name: Checkout base branch (${{ needs.check-changed.outputs.base_branch }})
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
ref: ${{ needs.check-changed.outputs.base_branch }}
path: base
# See the backward-compat job: the harness is new, so overlay it onto the base checkout.
- name: Overlay harness onto base checkout
run: |
rm -rf "base/$DATA_STRUCTURES_DIR/__fixtures__"
cp -r "current/$DATA_STRUCTURES_DIR/__fixtures__" "base/$DATA_STRUCTURES_DIR/__fixtures__"
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 22.x
- name: Setup pnpm
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
with:
# Both checkouts live in subdirectories, so there's no package.json at the workspace root;
# read the pnpm version from the current checkout's package.json (`packageManager`).
package_json_file: current/package.json
- name: Install and build @hexclave/shared (current)
working-directory: current
run: |
pnpm install --frozen-lockfile
pnpm exec turbo run build --filter=@hexclave/shared
- name: Install and build @hexclave/shared (base)
working-directory: base
run: |
pnpm install --frozen-lockfile
pnpm exec turbo run build --filter=@hexclave/shared
- name: Emit dump with current code
working-directory: current
run: pnpm -C apps/bulldozer-js exec tsx "$HARNESS_REL" emit "$GITHUB_WORKSPACE/new.json"
- name: Base code reads current-written dump
working-directory: base
run: pnpm -C apps/bulldozer-js exec tsx "$HARNESS_REL" verify "$GITHUB_WORKSPACE/new.json"