|
| 1 | +# Opt-in pre-merge 18.x upgrade harness (#24522, step 0 of the 18.x line). |
| 2 | +# |
| 3 | +# ## The gap this closes |
| 4 | +# |
| 5 | +# Ruling record 6266750393 on #15194 makes `main` the release line: every PR on |
| 6 | +# the 18.x line, once merged, leaves `main` releasable, and its R2 reads "a |
| 7 | +# database an 18.0 install created upgrades to that commit, boots, and keeps |
| 8 | +# its data" — a check each of the line's PRs passes BEFORE it merges. The |
| 9 | +# harness that answers R2 is a dogfood test, |
| 10 | +# `packages/qa/dogfood/test/upgrade-18x-harness.dogfood.test.ts`, and the |
| 11 | +# dogfood jobs in `ci.yml` run only on `main`'s full battery (THE TWO SPEEDS, |
| 12 | +# #22998): they skip `pull_request` and `merge_group`. So without this workflow |
| 13 | +# the line's PRs get their first R2 answer after the merge. |
| 14 | +# |
| 15 | +# This workflow adds the missing TRIGGER for that ONE file, and nothing else. |
| 16 | +# The test, its fixture and its assertions stay where they are, and the full |
| 17 | +# battery keeps running the same file with the rest of dogfood on `main`. |
| 18 | +# |
| 19 | +# ## Opt-in, label-gated — the cheapest wiring measured |
| 20 | +# |
| 21 | +# Measured on the step-0 branch (the card's report carries the readings): |
| 22 | +# |
| 23 | +# - the harness file alone: ~54 s of vitest wall (one 4-vCPU box, ~43 s of it |
| 24 | +# module import), against the dogfood suite's ~5 min per shard × 3; |
| 25 | +# - its build closure is the dogfood package's (`turbo run build |
| 26 | +# --filter=@objectstack/dogfood`, 66 tasks), which the turbo cache below |
| 27 | +# replays; cold, it is the step's whole cost. |
| 28 | +# |
| 29 | +# Three shapes were weighed. Running it on every PR taxes the whole repository |
| 30 | +# for one release line. Dispatching `ci.yml` by hand on a line PR's branch needs |
| 31 | +# no diff but runs the entire full battery (three dogfood shards, Temporal |
| 32 | +# Conformance, the console pin) for one file, and is a manual act with no |
| 33 | +# trigger a PR carries. The label lane runs one file, only where asked: the |
| 34 | +# shape `pack-smoke-optin.yml` already ruled for a pre-merge check that most |
| 35 | +# PRs do not need. The epic seat hangs `needs:upgrade-harness` on each 18.x |
| 36 | +# line PR; ⛔ never auto-applied, never in `.github/labeler.yml`. |
| 37 | +# |
| 38 | +# ## Wiring decisions (the `pack-smoke-optin.yml` ones, for the same reasons) |
| 39 | +# |
| 40 | +# `types:` restates GitHub's defaults beside `labeled` (naming `types:` |
| 41 | +# REPLACES the default set). The guard has two limbs: the PR carries the label |
| 42 | +# now, and on a `labeled` event the label just added is OURS — otherwise every |
| 43 | +# size label would start another run of an unchanged tree. `concurrency` sits at |
| 44 | +# JOB level, so a skipped run never cancels a real one. ⛔ No `merge_group:` |
| 45 | +# trigger: a `merge_group` event carries no pull request, so the label cannot be |
| 46 | +# read there, and this context is NOT a required one |
| 47 | +# (`scripts/check-required-contexts.mjs`) — the seat reads it before it queues a |
| 48 | +# line PR, the way the ruling's R2 asks. |
| 49 | + |
| 50 | +name: Upgrade Harness (opt-in) |
| 51 | + |
| 52 | +on: |
| 53 | + pull_request: |
| 54 | + branches: |
| 55 | + - main |
| 56 | + types: [opened, synchronize, reopened, labeled] |
| 57 | + |
| 58 | +permissions: |
| 59 | + contents: read |
| 60 | + |
| 61 | +jobs: |
| 62 | + upgrade-harness: |
| 63 | + name: 18.x upgrade harness (opt-in) |
| 64 | + if: >- |
| 65 | + contains(github.event.pull_request.labels.*.name, 'needs:upgrade-harness') |
| 66 | + && (github.event.action != 'labeled' || github.event.label.name == 'needs:upgrade-harness') |
| 67 | + runs-on: ubuntu-latest |
| 68 | + timeout-minutes: 30 |
| 69 | + concurrency: |
| 70 | + group: upgrade-harness-optin-${{ github.event.pull_request.number }} |
| 71 | + cancel-in-progress: true |
| 72 | + steps: |
| 73 | + # No `ref:` — the MERGE PREVIEW: what `main` will contain once this PR |
| 74 | + # lands, which is the commit R2 is a statement about. |
| 75 | + - name: Checkout the merge preview |
| 76 | + uses: actions/checkout@v7 |
| 77 | + |
| 78 | + - name: Setup Node.js |
| 79 | + uses: actions/setup-node@v7 |
| 80 | + with: |
| 81 | + node-version: '22' |
| 82 | + |
| 83 | + - name: Setup pnpm |
| 84 | + uses: ./.github/actions/setup-pnpm |
| 85 | + |
| 86 | + - name: Get pnpm store directory |
| 87 | + shell: bash |
| 88 | + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV |
| 89 | + |
| 90 | + - name: Setup pnpm cache |
| 91 | + uses: actions/cache@v6 |
| 92 | + with: |
| 93 | + path: ${{ env.STORE_PATH }} |
| 94 | + key: ${{ runner.os }}-pnpm-store-v3-${{ hashFiles('**/pnpm-lock.yaml') }} |
| 95 | + restore-keys: | |
| 96 | + ${{ runner.os }}-pnpm-store-v3- |
| 97 | +
|
| 98 | + # Restore-only: the dogfood job's entries (seeded from `main`) replay this |
| 99 | + # closure; a PR never writes the cache, as in ci.yml. |
| 100 | + - name: Restore Turbo cache |
| 101 | + uses: actions/cache/restore@v6 |
| 102 | + with: |
| 103 | + path: .turbo/cache |
| 104 | + key: ${{ runner.os }}-turbo-dogfood-1-main-${{ github.sha }} |
| 105 | + restore-keys: | |
| 106 | + ${{ runner.os }}-turbo-dogfood-1-main- |
| 107 | + ${{ runner.os }}-turbo-dogfood- |
| 108 | +
|
| 109 | + - name: Install dependencies |
| 110 | + run: pnpm install --frozen-lockfile |
| 111 | + |
| 112 | + - name: Build the dogfood package's dependency closure |
| 113 | + run: pnpm turbo run build --filter=@objectstack/dogfood --concurrency=4 |
| 114 | + |
| 115 | + # One file, named: the dogfood config's filter preflight refuses a path |
| 116 | + # that selects no test, so a renamed harness reds here instead of |
| 117 | + # passing over nothing. |
| 118 | + - name: Boot HEAD on the 18.0 fixture (R2) and check the v17 refusal (R3) |
| 119 | + run: pnpm --filter @objectstack/dogfood exec vitest run test/upgrade-18x-harness.dogfood.test.ts |
0 commit comments