* Cut CI toward two minutes * Bring CI under two minutes * Make approval lease test deterministic * Stabilize E2E dependency setup * Harden CI regression coverage
Showing the changes to .github (1 of 15 files). Show all
Unified · Split
| @@ -144,15 +144,18 @@ jobs: · 143 unmodified lines | |||
| 144 | 144 | ||
| 145 | test: | 145 | test: |
| 146 | name: Test | 146 | name: Test |
| 147 | runs-on: blacksmith-4vcpu-ubuntu-2404 | 147 | runs-on: blacksmith-16vcpu-ubuntu-2404 |
| 148 | timeout-minutes: 15 | 148 | 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. | ||
| 150 | env: | 152 | env: |
| 151 | TURBO_API: ${{ vars.TURBO_API }} | 153 | TURBO_API: ${{ vars.TURBO_API }} |
| 152 | TURBO_TEAM: ${{ vars.TURBO_TEAM }} | 154 | TURBO_TEAM: ${{ vars.TURBO_TEAM }} |
| 153 | TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} | 155 | TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} |
| 154 | TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} | 156 | 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 | ||
| 156 | steps: | 159 | steps: |
| 157 | - uses: actions/checkout@v4 | 160 | - uses: actions/checkout@v4 |
| 158 | 161 | ||
| @@ -184,6 +187,8 @@ jobs: · 25 unmodified lines | |||
| 184 | # no signal. This is just a few file reads plus greps, so ~zero cost. | 187 | # no signal. This is just a few file reads plus greps, so ~zero cost. |
| 185 | - run: bun run check:patches | 188 | - run: bun run check:patches |
| 186 | 189 | ||
| 190 | - run: bun run --cwd e2e test:ci-shard | ||
| 191 | |||
| 187 | - run: bun run test | 192 | - run: bun run test |
| 188 | 193 | ||
| 189 | e2e: | 194 | e2e: |
| @@ -192,25 +197,41 @@ jobs: · 2 unmodified lines | |||
| 192 | fail-fast: false | 197 | fail-fast: false |
| 193 | matrix: | 198 | matrix: |
| 194 | include: | 199 | include: |
| 195 | # PGlite is deliberately single-connection, and under a sustained | 200 | # The planner assigns every file exactly once using recorded slow-file |
| 196 | # multi-minute shard it can stop accepting postgres sockets. Keep | 201 | # durations plus a conservative weight for new tests. The cloud DB's |
| 197 | # every hermetic dev stack short: eight serial shards remove that | 202 | # connection teardown and concurrent socket protocol have dedicated |
| 198 | # lifetime-dependent failure and put cloud below the selfhost lane. | 203 | # regression tests; these shards balance wall clock, not hide retries. |
| 199 | - { target: cloud, shard: 1/8, shard-name: 1of8 } | 204 | - { target: cloud, shard-index: 1, shard-name: 1of16 } |
| 200 | - { target: cloud, shard: 2/8, shard-name: 2of8 } | 205 | - { target: cloud, shard-index: 2, shard-name: 2of16 } |
| 201 | - { target: cloud, shard: 3/8, shard-name: 3of8 } | 206 | - { target: cloud, shard-index: 3, shard-name: 3of16 } |
| 202 | - { target: cloud, shard: 4/8, shard-name: 4of8 } | 207 | - { target: cloud, shard-index: 4, shard-name: 4of16 } |
| 203 | - { target: cloud, shard: 5/8, shard-name: 5of8 } | 208 | - { target: cloud, shard-index: 5, shard-name: 5of16 } |
| 204 | - { target: cloud, shard: 6/8, shard-name: 6of8 } | 209 | - { target: cloud, shard-index: 6, shard-name: 6of16 } |
| 205 | - { target: cloud, shard: 7/8, shard-name: 7of8 } | 210 | - { target: cloud, shard-index: 7, shard-name: 7of16 } |
| 206 | - { target: cloud, shard: 8/8, shard-name: 8of8 } | 211 | - { target: cloud, shard-index: 8, shard-name: 8of16 } |
| 207 | # Selfhost shards the same way: each shard is its own runner booting | 212 | - { target: cloud, shard-index: 9, shard-name: 9of16 } |
| 208 | # its own fresh instance (own port block + data dir), so the | 213 | - { target: cloud, shard-index: 10, shard-name: 10of16 } |
| 209 | # project's shared-bootstrap-admin assumption stays intact per shard | 214 | - { target: cloud, shard-index: 11, shard-name: 11of16 } |
| 210 | # and `fileParallelism: false` still serializes within a shard. | 215 | - { target: cloud, shard-index: 12, shard-name: 12of16 } |
| 211 | - { target: selfhost, shard: 1/3, shard-name: 1of3 } | 216 | - { target: cloud, shard-index: 13, shard-name: 13of16 } |
| 212 | - { target: selfhost, shard: 2/3, shard-name: 2of3 } | 217 | - { target: cloud, shard-index: 14, shard-name: 14of16 } |
| 213 | - { target: selfhost, shard: 3/3, shard-name: 3of3 } | 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 } | ||
| 214 | runs-on: blacksmith-4vcpu-ubuntu-2404 | 235 | runs-on: blacksmith-4vcpu-ubuntu-2404 |
| 215 | timeout-minutes: 30 | 236 | timeout-minutes: 30 |
| 216 | steps: | 237 | steps: |
| @@ -246,26 +267,29 @@ jobs: · 29 unmodified lines | |||
| 246 | 267 | ||
| 247 | # Install from e2e so bunx resolves ITS pinned playwright (the version | 268 | # Install from e2e so bunx resolves ITS pinned playwright (the version |
| 248 | # the tests run against) rather than floating to the latest. | 269 | # 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. | ||
| 249 | - name: Install Playwright Chromium | 273 | - name: Install Playwright Chromium |
| 250 | run: bunx playwright install --with-deps chromium chromium-headless-shell | 274 | run: bunx playwright install chromium chromium-headless-shell |
| 251 | working-directory: e2e | 275 | working-directory: e2e |
| 252 | 276 | ||
| 253 | # The globalsetup boots the target's own dev server (ports are claimed | 277 | # Each target either boots its own shared dev server or lets each file own |
| 254 | # per checkout, so this is hermetic) and tears it down after the run. | 278 | # its server. Ports and data paths are hermetic in both cases. |
| 255 | # Do not retry scenarios: retries hide flakes and multiply slow timeout | 279 | # Do not retry scenarios: retries hide flakes and multiply slow timeout |
| 256 | # failures. The fixtures and process lifecycle are deterministic enough | 280 | # failures. The fixtures and process lifecycle are deterministic enough |
| 257 | # that the first result is the result. | 281 | # that the first result is the result. |
| 258 | - name: Run cloud scenarios | 282 | - name: Run cloud shard |
| 259 | if: matrix.target == 'cloud' | 283 | if: matrix.target == 'cloud' |
| 260 | env: | 284 | env: |
| 261 | MCP_SESSION_TIMEOUT_MS: "3000" | 285 | MCP_SESSION_TIMEOUT_MS: "3000" |
| 262 | MCP_PAUSED_SESSION_IDLE_TIMEOUT_MS: "6000" | 286 | 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'] }} |
| 264 | working-directory: e2e | 288 | working-directory: e2e |
| 265 | 289 | ||
| 266 | - name: Run selfhost scenarios | 290 | - name: Run scenarios |
| 267 | if: matrix.target == 'selfhost' | 291 | if: matrix.target != 'cloud' |
| 268 | run: bunx vitest run --project selfhost ${{ matrix.shard && format('--shard={0}', matrix.shard) || '' }} | 292 | run: bun scripts/run-ci-shard.ts ${{ matrix.target }} ${{ matrix['shard-index'] }} |
| 269 | working-directory: e2e | 293 | working-directory: e2e |
| 270 | 294 | ||
| 271 | # Failed runs keep their trace.zip / session.mp4 / step screenshots in | 295 | # Failed runs keep their trace.zip / session.mp4 / step screenshots in |
| @@ -278,56 +302,6 @@ jobs: · 6 unmodified lines | |||
| 278 | path: e2e/runs/ | 302 | path: e2e/runs/ |
| 279 | retention-days: 7 | 303 | retention-days: 7 |
| 280 | 304 | ||
| 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 | |||
| 331 | desktop-smoke: | 305 | desktop-smoke: |
| 332 | name: Desktop smoke build | 306 | name: Desktop smoke build |
| 333 | needs: changes | 307 | needs: changes |