@@ -106,15 +106,21 @@ jobs:
106106 echo "Smoke matrix: $json"
107107
108108 smoke :
109- name : Blender ${{ matrix.series }} smoke
109+ # The catalog (a cold Blender launch per happy path and per falsifier) is
110+ # split across three parallel jobs per series (#529): one serial job
111+ # took 15-40 minutes and grew two launches per series with every example.
112+ # Shard 0 also runs the template, harness-driver and canary steps, which
113+ # are not catalog rows.
114+ name : Blender ${{ matrix.series }} smoke (${{ matrix.shard }}/3)
110115 needs : resolve-matrix
111116 if : needs.resolve-matrix.result == 'success'
112117 runs-on : ubuntu-latest
113- timeout-minutes : 75 # happy paths + one falsifier per catalog row (~2x the old run)
118+ timeout-minutes : 45
114119 strategy :
115120 fail-fast : false
116121 matrix :
117122 series : ${{ fromJSON(needs.resolve-matrix.outputs.series) }}
123+ shard : [0, 1, 2] # keep in step with the K/3 below
118124 steps :
119125 - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
120126
@@ -203,17 +209,20 @@ jobs:
203209 || { echo "::error::version does not match series ${{ matrix.series }}"; exit 1; }
204210
205211 - name : Run in-Blender smoke driver
212+ if : matrix.shard == 0
206213 run : |
207214 set -euo pipefail
208215 mkdir -p "$RUNNER_TEMP/out"
209216 xvfb-run -a "$BLENDER" --background --python-exit-code 1 --python tests/smoke/run_smoke.py -- "$RUNNER_TEMP/out"
210217
211218 - name : Build template input scene
219+ if : matrix.shard == 0
212220 run : |
213221 set -euo pipefail
214222 xvfb-run -a "$BLENDER" --background --python-exit-code 1 --python tests/smoke/make_input.py -- "$RUNNER_TEMP/out/input.blend"
215223
216224 - name : Headless glTF template runs (exit 0, .glb produced)
225+ if : matrix.shard == 0
217226 run : |
218227 set -euo pipefail
219228 xvfb-run -a "$BLENDER" --background "$RUNNER_TEMP/out/input.blend" \
@@ -223,6 +232,7 @@ jobs:
223232 head -c4 "$RUNNER_TEMP/out/out.glb" | grep -q "glTF" || { echo "::error::not a glTF binary"; exit 1; }
224233
225234 - name : Headless template no-mesh path returns exit 5
235+ if : matrix.shard == 0
226236 run : |
227237 set +e
228238 xvfb-run -a "$BLENDER" --background "$RUNNER_TEMP/out/empty.blend" \
@@ -233,6 +243,7 @@ jobs:
233243 echo "no-mesh exit code = $code (correct)"
234244
235245 - name : Headless template applies the new modifier after the existing stack (#468)
246+ if : matrix.shard == 0
236247 run : |
237248 set -euo pipefail
238249 # stack.blend: a cube with a live SUBSURF (levels 1). SUBSURF then
@@ -245,18 +256,21 @@ jobs:
245256 "$RUNNER_TEMP/out/stack.glb" 48
246257
247258 - name : Extension template passes Blender's own manifest validator
259+ if : matrix.shard == 0
248260 run : |
249261 set -euo pipefail
250262 "$BLENDER" --command extension validate templates/extension-addon-template
251263
252264 - name : Build pipeline template fixture GLB
265+ if : matrix.shard == 0
253266 run : |
254267 set -euo pipefail
255268 xvfb-run -a "$BLENDER" --background --python-exit-code 1 --python tests/smoke/make_pipeline_glb.py -- \
256269 "$RUNNER_TEMP/out/pipeline_src.glb"
257270 test -s "$RUNNER_TEMP/out/pipeline_src.glb" || { echo "::error::pipeline fixture missing"; exit 1; }
258271
259272 - name : Headless AI asset pipeline template runs (exit 0, LODs produced)
273+ if : matrix.shard == 0
260274 run : |
261275 set -euo pipefail
262276 mkdir -p "$RUNNER_TEMP/out/pipeline"
@@ -271,6 +285,7 @@ jobs:
271285 head -c4 "$RUNNER_TEMP/out/pipeline/lod0.glb" | grep -q "glTF" || { echo "::error::lod0 not a glTF binary"; exit 1; }
272286
273287 - name : Headless pipeline template missing input returns exit 13
288+ if : matrix.shard == 0
274289 run : |
275290 set +e
276291 xvfb-run -a "$BLENDER" --background --python-exit-code 1 --python templates/ai-asset-pipeline-template/pipeline.py -- \
@@ -283,6 +298,7 @@ jobs:
283298 echo "missing-input exit code = $code (correct)"
284299
285300 - name : Pipeline template keeps every part of a shared-mesh, parented GLB (#462, # 467)
301+ if : matrix.shard == 0
286302 run : |
287303 set -euo pipefail
288304 # Root rotated 90 deg on X, scaled 2x, at z=3; Body plus two wheels
@@ -301,6 +317,7 @@ jobs:
301317 "$RUNNER_TEMP/out/parented_src.glb" "$RUNNER_TEMP/out/pipeline_parented/lod0.glb"
302318
303319 - name : Falsifier - pipeline template rejects a 2 km asset with exit 8
320+ if : matrix.shard == 0
304321 run : |
305322 set -euo pipefail
306323 xvfb-run -a "$BLENDER" --background --python-exit-code 1 --python tests/smoke/make_pipeline_glb.py -- \
@@ -316,6 +333,7 @@ jobs:
316333 echo "out-of-range extent exit code = $code (correct)"
317334
318335 - name : Headless render template runs (exit 0, PNG produced)
336+ if : matrix.shard == 0
319337 run : |
320338 set -euo pipefail
321339 # Cycles (CPU) so this is reliable on GPU-less runners; the EEVEE-id regression
@@ -329,6 +347,7 @@ jobs:
329347 # file would make summarize.py's "zero PASS is red" guard unreachable
330348 # (#536). Each one asserts its own verdict inline instead.
331349 - name : Canary - expected skip is SKIP not PASS
350+ if : matrix.shard == 0
332351 run : |
333352 set -euo pipefail
334353 python3 tests/smoke/run_example.py --no-record \
@@ -342,6 +361,7 @@ jobs:
342361 || { echo "::error::expected skip was not classified SKIP"; exit 1; }
343362
344363 - name : Canary - unexpected skip is red
364+ if : matrix.shard == 0
345365 run : |
346366 set +e
347367 python3 tests/smoke/run_example.py --no-record \
@@ -357,6 +377,7 @@ jobs:
357377 echo "unexpected skip exited 1 (correct)"
358378
359379 - name : Canary - uncaught exception is red
380+ if : matrix.shard == 0
360381 run : |
361382 set +e
362383 python3 tests/smoke/run_example.py --no-record \
@@ -371,6 +392,7 @@ jobs:
371392 echo "uncaught exception exited 1 (correct)"
372393
373394 - name : Canary - post-exit sidecar
395+ if : matrix.shard == 0
374396 run : |
375397 set -euo pipefail
376398 python3 tests/smoke/run_example.py --no-record \
@@ -385,6 +407,7 @@ jobs:
385407 || { echo "::error::post-exit sidecar canary did not PASS"; exit 1; }
386408
387409 - name : Canary - missing sidecar is red
410+ if : matrix.shard == 0
388411 run : |
389412 set +e
390413 python3 tests/smoke/run_example.py --no-record \
@@ -407,6 +430,7 @@ jobs:
407430 python3 tests/smoke/run_catalog.py \
408431 --blender "$BLENDER" \
409432 --series "${{ matrix.series }}" \
433+ --shard "${{ matrix.shard }}/3" \
410434 --status "$BDT_SMOKE_STATUS" \
411435 --out "$RUNNER_TEMP/out" \
412436 --xvfb \
0 commit comments