77 - main
88
99concurrency :
10- group : ci-${{ github.event_name == 'push' && format('{0}-{1}', github.ref, github.sha) || github. ref }}
11- cancel-in-progress : ${{ github.event_name == 'pull_request' }}
10+ group : ci-${{ github.ref }}
11+ cancel-in-progress : true
1212
1313jobs :
14+ changes :
15+ name : Changed paths
16+ runs-on : blacksmith-4vcpu-ubuntu-2404
17+ timeout-minutes : 10
18+ outputs :
19+ desktop_smoke : ${{ github.event_name != 'pull_request' || steps.filter.outputs.desktop_smoke == 'true' }}
20+ selfhost_docker_smoke : ${{ github.event_name != 'pull_request' || steps.filter.outputs.selfhost_docker_smoke == 'true' }}
21+ steps :
22+ - uses : actions/checkout@v4
23+
24+ - name : Filter changed paths
25+ id : filter
26+ if : github.event_name == 'pull_request'
27+ uses : dorny/paths-filter@v3
28+ with :
29+ filters : |
30+ desktop_smoke:
31+ - ".github/workflows/**"
32+ - "bun.lock"
33+ - "package.json"
34+ - "turbo.json"
35+ - "apps/desktop/**"
36+ - "apps/local/**"
37+ - "apps/cli/**"
38+ - "packages/app/**"
39+ - "packages/core/**"
40+ - "packages/hosts/mcp/**"
41+ - "packages/kernel/runtime-quickjs/**"
42+ - "packages/plugins/**"
43+ - "packages/react/**"
44+ selfhost_docker_smoke:
45+ - ".github/workflows/**"
46+ - ".dockerignore"
47+ - "bun.lock"
48+ - "package.json"
49+ - "turbo.json"
50+ - "apps/host-selfhost/**"
51+ - "packages/app/**"
52+ - "packages/core/**"
53+ - "packages/hosts/mcp/**"
54+ - "packages/kernel/runtime-quickjs/**"
55+ - "packages/plugins/**"
56+ - "packages/react/**"
57+
1458 format :
1559 name : Format
1660 runs-on : blacksmith-4vcpu-ubuntu-2404
61+ timeout-minutes : 10
1762 steps :
1863 - uses : actions/checkout@v4
1964
@@ -22,27 +67,21 @@ jobs:
2267 bun-version : 1.3.11
2368
2469 - name : Cache Bun package cache
25- uses : actions /cache@v4
70+ uses : useblacksmith /cache@v5
2671 with :
2772 path : ~/.bun/install/cache
2873 key : ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
2974 restore-keys : |
3075 ${{ runner.os }}-bun-1.3.11-
3176
32- # No prebuilt better-sqlite3 binary matches this runner, so `bun install`
33- # builds it from source via node-gyp, whose undici needs Node 22.10+
34- # (webidl.markAsUncloneable). Pin the same runtime the test/e2e jobs use.
35- - uses : actions/setup-node@v4
36- with :
37- node-version : 22
38-
39- - run : bun install --frozen-lockfile
77+ - run : bun install --frozen-lockfile --ignore-scripts
4078
4179 - run : bun run format:check
4280
4381 lint :
4482 name : Lint
4583 runs-on : blacksmith-4vcpu-ubuntu-2404
84+ timeout-minutes : 10
4685 steps :
4786 - uses : actions/checkout@v4
4887
@@ -51,27 +90,21 @@ jobs:
5190 bun-version : 1.3.11
5291
5392 - name : Cache Bun package cache
54- uses : actions /cache@v4
93+ uses : useblacksmith /cache@v5
5594 with :
5695 path : ~/.bun/install/cache
5796 key : ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
5897 restore-keys : |
5998 ${{ runner.os }}-bun-1.3.11-
6099
61- # No prebuilt better-sqlite3 binary matches this runner, so `bun install`
62- # builds it from source via node-gyp, whose undici needs Node 22.10+
63- # (webidl.markAsUncloneable). Pin the same runtime the test/e2e jobs use.
64- - uses : actions/setup-node@v4
65- with :
66- node-version : 22
67-
68- - run : bun install --frozen-lockfile
100+ - run : bun install --frozen-lockfile --ignore-scripts
69101
70102 - run : bun run lint
71103
72104 typecheck :
73105 name : Typecheck
74106 runs-on : blacksmith-4vcpu-ubuntu-2404
107+ timeout-minutes : 15
75108 steps :
76109 - uses : actions/checkout@v4
77110
80113 bun-version : 1.3.11
81114
82115 - name : Cache Bun package cache
83- uses : actions /cache@v4
116+ uses : useblacksmith /cache@v5
84117 with :
85118 path : ~/.bun/install/cache
86119 key : ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
@@ -89,10 +122,19 @@ jobs:
89122
90123 # No prebuilt better-sqlite3 binary matches this runner, so `bun install`
91124 # builds it from source via node-gyp, whose undici needs Node 22.10+
92- # (webidl.markAsUncloneable). Pin the same runtime the test/e2e jobs use .
125+ # (webidl.markAsUncloneable). Pin the same Node 24 runtime used by release .
93126 - uses : actions/setup-node@v4
94127 with :
95- node-version : 22
128+ node-version : 24
129+
130+ - name : Cache Turbo task cache
131+ uses : useblacksmith/cache@v5
132+ with :
133+ path : .turbo
134+ key : ${{ runner.os }}-turbo-typecheck-${{ hashFiles('bun.lock', 'turbo.json') }}
135+ restore-keys : |
136+ ${{ runner.os }}-turbo-typecheck-
137+ ${{ runner.os }}-turbo-
96138
97139 - run : bun install --frozen-lockfile
98140
@@ -101,8 +143,10 @@ jobs:
101143 test :
102144 name : Test
103145 runs-on : blacksmith-4vcpu-ubuntu-2404
146+ timeout-minutes : 15
147+ # Tuned for Blacksmith's 4 vCPU runners.
104148 env :
105- TURBO_TEST_CONCURRENCY : 3
149+ TURBO_TEST_CONCURRENCY : 4
106150 steps :
107151 - uses : actions/checkout@v4
108152
@@ -111,7 +155,7 @@ jobs:
111155 bun-version : 1.3.11
112156
113157 - name : Cache Bun package cache
114- uses : actions /cache@v4
158+ uses : useblacksmith /cache@v5
115159 with :
116160 path : ~/.bun/install/cache
117161 key : ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
@@ -120,10 +164,19 @@ jobs:
120164
121165 # apps/cloud's test script invokes `node` directly; undici 8.x (pulled
122166 # in by @cloudflare/vitest-pool-workers) calls webidl.markAsUncloneable
123- # which only exists in Node 22.10+. Pin a known-good runtime.
167+ # which only exists in Node 22.10+. Pin the Node 24 CI runtime.
124168 - uses : actions/setup-node@v4
125169 with :
126- node-version : 22
170+ node-version : 24
171+
172+ - name : Cache Turbo task cache
173+ uses : useblacksmith/cache@v5
174+ with :
175+ path : .turbo
176+ key : ${{ runner.os }}-turbo-test-${{ hashFiles('bun.lock', 'turbo.json') }}
177+ restore-keys : |
178+ ${{ runner.os }}-turbo-test-
179+ ${{ runner.os }}-turbo-
127180
128181 - run : bun install --frozen-lockfile
129182
@@ -135,24 +188,18 @@ jobs:
135188 fail-fast : false
136189 matrix :
137190 include :
138- # Cloud is SHARDED: each shard boots its own fresh dev stack. The
139- # cloud dev server degrades after a few minutes of sustained suite
140- # load on 2-core runners (the SSE/OTel memory growth being
141- # instrumented on main) — requests start failing partway through and
142- # everything after dies with connection errors. Short shards on
143- # fresh boots stay under that threshold; re-merge into fewer jobs
144- # once the degradation is fixed.
145- - { target: cloud, shard: 1/8, shard-name: 1of8 }
146- - { target: cloud, shard: 2/8, shard-name: 2of8 }
147- - { target: cloud, shard: 3/8, shard-name: 3of8 }
148- - { target: cloud, shard: 4/8, shard-name: 4of8 }
149- - { target: cloud, shard: 5/8, shard-name: 5of8 }
150- - { target: cloud, shard: 6/8, shard-name: 6of8 }
151- - { target: cloud, shard: 7/8, shard-name: 7of8 }
152- - { target: cloud, shard: 8/8, shard-name: 8of8 }
191+ # Each cloud shard boots its own fresh dev stack. On 4 vCPU runners,
192+ # four fatter shards keep the longest shard below selfhost while saving
193+ # four runner boots and four warm cache restores.
194+ - { target: cloud, shard: 1/4, shard-name: 1of4 }
195+ - { target: cloud, shard: 2/4, shard-name: 2of4 }
196+ - { target: cloud, shard: 3/4, shard-name: 3of4 }
197+ - { target: cloud, shard: 4/4, shard-name: 4of4 }
153198 - target : selfhost
154199 runs-on : blacksmith-4vcpu-ubuntu-2404
155200 timeout-minutes : 30
201+ env :
202+ E2E_MCP_FAST_IDLE_TEARDOWN : " true"
156203 steps :
157204 - uses : actions/checkout@v4
158205
@@ -161,26 +208,28 @@ jobs:
161208 bun-version : 1.3.11
162209
163210 - name : Cache Bun package cache
164- uses : actions /cache@v4
211+ uses : useblacksmith /cache@v5
165212 with :
166213 path : ~/.bun/install/cache
167214 key : ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
168215 restore-keys : |
169216 ${{ runner.os }}-bun-1.3.11-
170217
171218 # The dev stacks spawn Node sidecars (vite/workerd tooling); pin the
172- # same known-good runtime the unit-test job uses .
219+ # same Node 24 runtime the release and publish workflows use .
173220 - uses : actions/setup-node@v4
174221 with :
175- node-version : 22
222+ node-version : 24
176223
177224 - run : bun install --frozen-lockfile
178225
179226 - name : Cache Playwright browsers
180- uses : actions /cache@v4
227+ uses : useblacksmith /cache@v5
181228 with :
182229 path : ~/.cache/ms-playwright
183230 key : ${{ runner.os }}-playwright-1.60.0
231+ restore-keys : |
232+ ${{ runner.os }}-playwright-
184233
185234 # Install from e2e so bunx resolves ITS pinned playwright (the version
186235 # the tests run against) rather than floating to the latest.
@@ -190,7 +239,7 @@ jobs:
190239
191240 # The globalsetup boots the target's own dev server (ports are claimed
192241 # per checkout, so this is hermetic) and tears it down after the run.
193- # --retry=2: browser scenarios time out sporadically on 2-core runners
242+ # --retry=2: browser scenarios can still hit isolated waitFor timeouts
194243 # (single-test waitFor timeouts, not systemic failures); a retry on the
195244 # same booted stack clears them.
196245 - name : Run ${{ matrix.target }} scenarios
@@ -222,26 +271,28 @@ jobs:
222271 bun-version : 1.3.11
223272
224273 - name : Cache Bun package cache
225- uses : actions /cache@v4
274+ uses : useblacksmith /cache@v5
226275 with :
227276 path : ~/.bun/install/cache
228277 key : ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
229278 restore-keys : |
230279 ${{ runner.os }}-bun-1.3.11-
231280
232281 # The local scenarios boot a real `executor web` (which spawns a Node
233- # sidecar) and some drive a browser, so pin Node 22 and install Chromium.
282+ # sidecar) and some drive a browser, so pin Node 24 and install Chromium.
234283 - uses : actions/setup-node@v4
235284 with :
236- node-version : 22
285+ node-version : 24
237286
238287 - run : bun install --frozen-lockfile
239288
240289 - name : Cache Playwright browsers
241- uses : actions /cache@v4
290+ uses : useblacksmith /cache@v5
242291 with :
243292 path : ~/.cache/ms-playwright
244293 key : ${{ runner.os }}-playwright-1.60.0
294+ restore-keys : |
295+ ${{ runner.os }}-playwright-
245296
246297 # `chromium` and the new `chromium-headless-shell` ship as separate
247298 # downloads; the browser-driven scenarios launch the headless shell.
@@ -265,7 +316,10 @@ jobs:
265316
266317 desktop-smoke :
267318 name : Desktop smoke build
319+ needs : changes
320+ if : needs.changes.outputs.desktop_smoke == 'true'
268321 runs-on : blacksmith-4vcpu-ubuntu-2404
322+ timeout-minutes : 20
269323 steps :
270324 - uses : actions/checkout@v4
271325
@@ -274,7 +328,7 @@ jobs:
274328 bun-version : 1.3.11
275329
276330 - name : Cache Bun package cache
277- uses : actions /cache@v4
331+ uses : useblacksmith /cache@v5
278332 with :
279333 path : ~/.bun/install/cache
280334 key : ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
@@ -283,10 +337,10 @@ jobs:
283337
284338 # No prebuilt better-sqlite3 binary matches this runner, so `bun install`
285339 # builds it from source via node-gyp, whose undici needs Node 22.10+
286- # (webidl.markAsUncloneable). Pin the same runtime the test/e2e jobs use .
340+ # (webidl.markAsUncloneable). Pin the same Node 24 CI runtime .
287341 - uses : actions/setup-node@v4
288342 with :
289- node-version : 22
343+ node-version : 24
290344
291345 - run : bun install --frozen-lockfile
292346
@@ -305,7 +359,10 @@ jobs:
305359
306360 selfhost-docker-smoke :
307361 name : Self-host Docker image
362+ needs : changes
363+ if : needs.changes.outputs.selfhost_docker_smoke == 'true'
308364 runs-on : blacksmith-4vcpu-ubuntu-2404
365+ timeout-minutes : 20
309366 steps :
310367 - uses : actions/checkout@v4
311368
0 commit comments