ci: add Blender smoke-test workflow (runs examples in real Blender) #1
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Blender Smoke Test | |
| # Executes the snippets' and skills' headline examples inside REAL Blender, headless, | |
| # on the current stable (5.1.x) and the active LTS (4.5.x), and fails on any error or | |
| # empty-output assertion. py_compile (in validate.yml) cannot catch API-level regressions | |
| # like the EEVEE-id inversion, the slotted-actions boundary, the driver TypeError, or the | |
| # dead SDF link -- this gate runs the code so those surface in CI, not in users' files. | |
| on: | |
| workflow_dispatch: {} | |
| schedule: | |
| - cron: "0 7 * * 1" # weekly, Monday 07:00 UTC | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: blender-smoke-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| smoke: | |
| name: Blender ${{ matrix.series }} smoke | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - series: "5.1" # current stable | |
| - series: "4.5" # active LTS | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Resolve and download Blender ${{ matrix.series }} | |
| run: | | |
| set -euo pipefail | |
| series="${{ matrix.series }}" | |
| base="https://download.blender.org/release/Blender${series}/" | |
| echo "Listing $base" | |
| # pick the highest point release for this series (linux x64 portable) | |
| file=$(curl -fsSL "$base" \ | |
| | grep -oE "blender-${series}\.[0-9]+-linux-x64\.tar\.xz" \ | |
| | sort -V | uniq | tail -1) | |
| if [ -z "$file" ]; then | |
| echo "::error::Could not resolve a linux-x64 build for Blender ${series} at $base" | |
| exit 1 | |
| fi | |
| url="${base}${file}" | |
| echo "Downloading $url" | |
| mkdir -p "$RUNNER_TEMP/bl" | |
| curl -fSL --retry 3 -o "$RUNNER_TEMP/bl.tar.xz" "$url" | |
| tar -xf "$RUNNER_TEMP/bl.tar.xz" -C "$RUNNER_TEMP/bl" | |
| bl=$(find "$RUNNER_TEMP/bl" -maxdepth 2 -type f -name blender | head -1) | |
| if [ -z "$bl" ]; then | |
| echo "::error::blender binary not found after extraction" | |
| exit 1 | |
| fi | |
| echo "BLENDER=$bl" >> "$GITHUB_ENV" | |
| - name: Print Blender version | |
| run: | | |
| set -euo pipefail | |
| "$BLENDER" --version | head -1 | |
| # series guard: confirm we actually got the matrix series | |
| "$BLENDER" --version | head -1 | grep -q "Blender ${{ matrix.series }}\." \ | |
| || { echo "::error::version does not match series ${{ matrix.series }}"; exit 1; } | |
| - name: Run in-Blender smoke driver | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$RUNNER_TEMP/out" | |
| "$BLENDER" --background --python tests/smoke/run_smoke.py -- "$RUNNER_TEMP/out" | |
| - name: Build template input scene | |
| run: | | |
| set -euo pipefail | |
| "$BLENDER" --background --python tests/smoke/make_input.py -- "$RUNNER_TEMP/out/input.blend" | |
| - name: Headless glTF template runs (exit 0, .glb produced) | |
| run: | | |
| set -euo pipefail | |
| "$BLENDER" --background "$RUNNER_TEMP/out/input.blend" \ | |
| --python tests/smoke/tmpl_gltf.py -- \ | |
| --output "$RUNNER_TEMP/out/out.glb" --apply-modifier SUBSURF | |
| test -s "$RUNNER_TEMP/out/out.glb" || { echo "::error::glTF output missing/empty"; exit 1; } | |
| head -c4 "$RUNNER_TEMP/out/out.glb" | grep -q "glTF" || { echo "::error::not a glTF binary"; exit 1; } | |
| - name: Headless template no-mesh path returns exit 2 | |
| run: | | |
| set +e | |
| "$BLENDER" --background "$RUNNER_TEMP/out/empty.blend" \ | |
| --python tests/smoke/tmpl_gltf.py -- --output "$RUNNER_TEMP/out/none.glb" | |
| code=$? | |
| set -e | |
| [ "$code" -eq 2 ] || { echo "::error::expected exit 2 for no-mesh input, got $code"; exit 1; } | |
| echo "no-mesh exit code = $code (correct)" | |
| - name: Headless render template runs (exit 0, PNG produced) | |
| run: | | |
| set -euo pipefail | |
| # Cycles (CPU) so this is reliable on GPU-less runners; the EEVEE-id regression | |
| # itself is gated in run_smoke.py via engine assignment. | |
| "$BLENDER" --background "$RUNNER_TEMP/out/input.blend" \ | |
| --python tests/smoke/tmpl_render.py -- \ | |
| --output "$RUNNER_TEMP/out/render.png" --engine CYCLES | |
| test -s "$RUNNER_TEMP/out/render.png" || { echo "::error::render PNG missing/empty"; exit 1; } |