History

Bring CI under two minutes (#1644)

* Cut CI toward two minutes * Bring CI under two minutes * Make approval lease test deterministic * Stabilize E2E dependency setup * Harden CI regression coverage

Rhys Sullivan committed 2 months ago

Commit 86acd381 parent 9ecc7cb15 files · +661 −93Browse files

Showing the changes to .github (1 of 15 files). Show all

Unified · Split

.github/workflows/ci.yml+54−80
@@ -144,15 +144,18 @@ jobs: · 143 unmodified lines
144144
145145 test:
146146 name: Test
147− runs-on: blacksmith-4vcpu-ubuntu-2404
147+ runs-on: blacksmith-16vcpu-ubuntu-2404
148148 timeout-minutes: 15
149− # Tuned for Blacksmith's 4 vCPU runners.
149+ # Run eight independent packages at once and cap each Vitest process at two
150+ # workers. This uses all 16 vCPUs without every nested runner seeing the
151+ # whole machine and oversubscribing it.
150152 env:
151153 TURBO_API: ${{ vars.TURBO_API }}
152154 TURBO_TEAM: ${{ vars.TURBO_TEAM }}
153155 TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
154156 TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
155− TURBO_TEST_CONCURRENCY: 4
157+ TURBO_TEST_CONCURRENCY: 8
158+ VITEST_MAX_WORKERS: 2
156159 steps:
157160 - uses: actions/checkout@v4
158161
@@ -184,6 +187,8 @@ jobs: · 25 unmodified lines
184187 # no signal. This is just a few file reads plus greps, so ~zero cost.
185188 - run: bun run check:patches
186189
190+ - run: bun run --cwd e2e test:ci-shard
191+
187192 - run: bun run test
188193
189194 e2e:
@@ -192,25 +197,41 @@ jobs: · 2 unmodified lines
192197 fail-fast: false
193198 matrix:
194199 include:
195− # PGlite is deliberately single-connection, and under a sustained
196− # multi-minute shard it can stop accepting postgres sockets. Keep
197− # every hermetic dev stack short: eight serial shards remove that
198− # lifetime-dependent failure and put cloud below the selfhost lane.
199− - { target: cloud, shard: 1/8, shard-name: 1of8 }
200− - { target: cloud, shard: 2/8, shard-name: 2of8 }
201− - { target: cloud, shard: 3/8, shard-name: 3of8 }
202− - { target: cloud, shard: 4/8, shard-name: 4of8 }
203− - { target: cloud, shard: 5/8, shard-name: 5of8 }
204− - { target: cloud, shard: 6/8, shard-name: 6of8 }
205− - { target: cloud, shard: 7/8, shard-name: 7of8 }
206− - { target: cloud, shard: 8/8, shard-name: 8of8 }
207− # Selfhost shards the same way: each shard is its own runner booting
208− # its own fresh instance (own port block + data dir), so the
209− # project's shared-bootstrap-admin assumption stays intact per shard
210− # and `fileParallelism: false` still serializes within a shard.
211− - { target: selfhost, shard: 1/3, shard-name: 1of3 }
212− - { target: selfhost, shard: 2/3, shard-name: 2of3 }
213− - { target: selfhost, shard: 3/3, shard-name: 3of3 }
200+ # The planner assigns every file exactly once using recorded slow-file
201+ # durations plus a conservative weight for new tests. The cloud DB's
202+ # connection teardown and concurrent socket protocol have dedicated
203+ # regression tests; these shards balance wall clock, not hide retries.
204+ - { target: cloud, shard-index: 1, shard-name: 1of16 }
205+ - { target: cloud, shard-index: 2, shard-name: 2of16 }
206+ - { target: cloud, shard-index: 3, shard-name: 3of16 }
207+ - { target: cloud, shard-index: 4, shard-name: 4of16 }
208+ - { target: cloud, shard-index: 5, shard-name: 5of16 }
209+ - { target: cloud, shard-index: 6, shard-name: 6of16 }
210+ - { target: cloud, shard-index: 7, shard-name: 7of16 }
211+ - { target: cloud, shard-index: 8, shard-name: 8of16 }
212+ - { target: cloud, shard-index: 9, shard-name: 9of16 }
213+ - { target: cloud, shard-index: 10, shard-name: 10of16 }
214+ - { target: cloud, shard-index: 11, shard-name: 11of16 }
215+ - { target: cloud, shard-index: 12, shard-name: 12of16 }
216+ - { target: cloud, shard-index: 13, shard-name: 13of16 }
217+ - { target: cloud, shard-index: 14, shard-name: 14of16 }
218+ - { target: cloud, shard-index: 15, shard-name: 15of16 }
219+ - { target: cloud, shard-index: 16, shard-name: 16of16 }
220+ - { target: selfhost, shard-index: 1, shard-name: 1of10 }
221+ - { target: selfhost, shard-index: 2, shard-name: 2of10 }
222+ - { target: selfhost, shard-index: 3, shard-name: 3of10 }
223+ - { target: selfhost, shard-index: 4, shard-name: 4of10 }
224+ - { target: selfhost, shard-index: 5, shard-name: 5of10 }
225+ - { target: selfhost, shard-index: 6, shard-name: 6of10 }
226+ - { target: selfhost, shard-index: 7, shard-name: 7of10 }
227+ - { target: selfhost, shard-index: 8, shard-name: 8of10 }
228+ - { target: selfhost, shard-index: 9, shard-name: 9of10 }
229+ - { target: selfhost, shard-index: 10, shard-name: 10of10 }
230+ # Local files own their server, browser and data directory. Separate
231+ # runners preserve that isolation while removing its 69-second serial
232+ # lane from the two-minute critical path.
233+ - { target: local, shard-index: 1, shard-name: 1of2 }
234+ - { target: local, shard-index: 2, shard-name: 2of2 }
214235 runs-on: blacksmith-4vcpu-ubuntu-2404
215236 timeout-minutes: 30
216237 steps:
@@ -246,26 +267,29 @@ jobs: · 29 unmodified lines
246267
247268 # Install from e2e so bunx resolves ITS pinned playwright (the version
248269 # the tests run against) rather than floating to the latest.
270+ # Blacksmith's Ubuntu image carries the official GitHub runner system
271+ # dependencies. Restore the pinned browser binaries without apt-updating
272+ # every matrix machine.
249273 - name: Install Playwright Chromium
250− run: bunx playwright install --with-deps chromium chromium-headless-shell
274+ run: bunx playwright install chromium chromium-headless-shell
251275 working-directory: e2e
252276
253− # The globalsetup boots the target's own dev server (ports are claimed
254− # per checkout, so this is hermetic) and tears it down after the run.
277+ # Each target either boots its own shared dev server or lets each file own
278+ # its server. Ports and data paths are hermetic in both cases.
255279 # Do not retry scenarios: retries hide flakes and multiply slow timeout
256280 # failures. The fixtures and process lifecycle are deterministic enough
257281 # that the first result is the result.
258− - name: Run cloud scenarios
282+ - name: Run cloud shard
259283 if: matrix.target == 'cloud'
260284 env:
261285 MCP_SESSION_TIMEOUT_MS: "3000"
262286 MCP_PAUSED_SESSION_IDLE_TIMEOUT_MS: "6000"
263− run: bunx vitest run --project cloud ${{ matrix.shard && format('--shard={0}', matrix.shard) || '' }}
287+ run: bun scripts/run-ci-shard.ts cloud ${{ matrix['shard-index'] }}
264288 working-directory: e2e
265289
266− - name: Run selfhost scenarios
267− if: matrix.target == 'selfhost'
268− run: bunx vitest run --project selfhost ${{ matrix.shard && format('--shard={0}', matrix.shard) || '' }}
290+ - name: Run scenarios
291+ if: matrix.target != 'cloud'
292+ run: bun scripts/run-ci-shard.ts ${{ matrix.target }} ${{ matrix['shard-index'] }}
269293 working-directory: e2e
270294
271295 # Failed runs keep their trace.zip / session.mp4 / step screenshots in
@@ -278,56 +302,6 @@ jobs: · 6 unmodified lines
278302 path: e2e/runs/
279303 retention-days: 7
280304
281− e2e-local:
282− name: E2E (local)
283− runs-on: blacksmith-4vcpu-ubuntu-2404
284− timeout-minutes: 20
285− steps:
286− - uses: actions/checkout@v4
287−
288− - uses: oven-sh/setup-bun@v2
289− with:
290− bun-version: 1.3.11
291−
292− - name: Cache Bun package cache
293− uses: actions/cache@v4
294− with:
295− path: ~/.bun/install/cache
296− key: ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
297− restore-keys: |
298− ${{ runner.os }}-bun-1.3.11-
299−
300− # The local scenarios boot a real `executor web` (which spawns a Node
301− # sidecar) and some drive a browser, so pin Node 24 and install Chromium.
302− - uses: actions/setup-node@v4
303− with:
304− node-version: 24
305−
306− - run: bun install --frozen-lockfile
307−
308− - name: Cache Playwright browsers
309− uses: actions/cache@v4
310− with:
311− path: ~/.cache/ms-playwright
312− key: ${{ runner.os }}-playwright-1.60.0
313− restore-keys: |
314− ${{ runner.os }}-playwright-
315−
316− # `chromium` and the new `chromium-headless-shell` ship as separate
317− # downloads; the browser-driven scenarios launch the headless shell.
318− # Install from e2e so bunx resolves ITS pinned playwright (the version the
319− # tests run against) rather than floating to the latest, which would fetch
320− # a browser build the test runtime does not look for.
321− - name: Install Playwright Chromium
322− run: bunx playwright install --with-deps chromium chromium-headless-shell
323− working-directory: e2e
324−
325− # Each scenario owns its server, browser, data directory, and descendants;
326− # run the complete hermetic suite on PRs without scenario retries.
327− - name: Run local scenarios
328− run: bunx vitest run --project local
329− working-directory: e2e
330−
331305 desktop-smoke:
332306 name: Desktop smoke build
333307 needs: changes