Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/actions/native-rust-cache/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Restore the Rust crates of the native build
description: >-
`native:build` compiles the physics crate and the ui-overlay crate. Rust took 112 s of 173 s in the
starter-linux x64 build (job 124904693949). Only a push saves, so the pull-request and queue boards
spend no cache budget. Cargo fingerprints content, so an older restore rebuilds only what moved.
runs:
using: composite
steps:
# A `-sys` crate does not rerun when a system library changes, so the image and the two libraries
# it links to name the prefix, and no restore crosses them.
- id: identity
shell: bash
run: |
libs=""
if command -v dpkg-query >/dev/null; then
libs=$( (dpkg-query -W libwebkit2gtk-4.1-dev libgtk-3-dev 2>/dev/null || true) | sha256sum | cut -c1-12)
fi
rust=$( (rustc --version 2>/dev/null || echo none) | tr -c 'a-zA-Z0-9.\n' '-')
echo "prefix=cargo-v1-${{ runner.os }}-${{ runner.arch }}-${ImageVersion:-self}-$rust-$libs-" >> "$GITHUB_OUTPUT"
- if: github.event_name == 'push'
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git/db
packages/runtime-native/.runtime/physics-target
packages/runtime-native/native/ui-overlay/target
key: ${{ steps.identity.outputs.prefix }}${{ hashFiles('packages/runtime-native/native/**/Cargo.lock', 'packages/runtime-native/native/**/Cargo.toml', 'packages/runtime-native/native/**/*.rs', 'packages/runtime-native/scripts/build-native-physics.mjs', 'packages/runtime-native/scripts/native-build.mjs') }}
restore-keys: ${{ steps.identity.outputs.prefix }}
- if: github.event_name != 'push'
uses: actions/cache/restore@v4
with:
path: |
~/.cargo/registry
~/.cargo/git/db
packages/runtime-native/.runtime/physics-target
packages/runtime-native/native/ui-overlay/target
key: ${{ steps.identity.outputs.prefix }}${{ hashFiles('packages/runtime-native/native/**/Cargo.lock', 'packages/runtime-native/native/**/Cargo.toml', 'packages/runtime-native/native/**/*.rs', 'packages/runtime-native/scripts/build-native-physics.mjs', 'packages/runtime-native/scripts/native-build.mjs') }}
restore-keys: ${{ steps.identity.outputs.prefix }}
27 changes: 27 additions & 0 deletions .github/actions/playwright-chromium/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ runs:
for source in /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources; do
[ ! -f "$source" ] || sudo sed -i 's|http://azure.archive.ubuntu.com/ubuntu|https://archive.ubuntu.com/ubuntu|g; s|https://azure.archive.ubuntu.com/ubuntu|https://archive.ubuntu.com/ubuntu|g' "$source"
done
# apt checks each .deb against its index, so a stale file is only unused. A push saves.
- name: Record the apt package cache identity
id: debs
shell: bash
run: |
echo "key=playwright-debs-v1-${{ runner.os }}-${{ runner.arch }}-${ImageVersion:-self}-${{ steps.version.outputs.value }}" >> "$GITHUB_OUTPUT"
mkdir -p ~/playwright-debs
- name: Restore the Chromium system packages
id: debs-restore
uses: actions/cache/restore@v4
with:
path: ~/playwright-debs
key: ${{ steps.debs.outputs.key }}
- name: Offer the restored packages to apt
shell: bash
run: sudo find ~/playwright-debs -name '*.deb' -exec cp -t /var/cache/apt/archives/ {} +
# The browser binary caches; its shared libraries are apt packages that do not, so
# `install-deps` still runs. On a cache hit that is the only work left.
# It takes 11-114 s; a mirror that trickles held it 29 minutes (run 41249368283), so each
Expand All @@ -34,3 +50,14 @@ runs:
pnpm exec playwright install chromium
fi
sh scripts/retry-download.sh sudo timeout --kill-after=15 240 env "PATH=$PATH" node_modules/.bin/playwright install-deps chromium
- name: Keep the packages apt fetched
if: github.event_name == 'push' && steps.debs-restore.outputs.cache-hit != 'true'
shell: bash
run: find /var/cache/apt/archives -maxdepth 1 -name '*.deb' -exec cp -t ~/playwright-debs/ {} +
- name: Save the Chromium system packages
if: github.event_name == 'push' && steps.debs-restore.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@v4
with:
path: ~/playwright-debs
key: ${{ steps.debs.outputs.key }}
43 changes: 29 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,9 @@ jobs:
pnpm --dir "$target" install
pnpm --dir "$target" run check
- name: Run the template's GPU-free scenarios
# Owner cut (2026-11-01): the board plays the starter through golden-path and keeps its
# pristine check above; its scenarios, 21 engine guards and parity pose run on the nightly.
if: matrix.template != 'starter' || github.event_name == 'schedule'
run: |
set -euo pipefail
target="$RUNNER_TEMP/threenative-${{ matrix.template }}"
Expand All @@ -930,7 +933,9 @@ jobs:
extras="$GITHUB_WORKSPACE/packages/create-threenative/template-playtests/${{ matrix.template }}"
if [ -d "$extras" ]; then cp -r "$extras/." "$target/playtests/"; fi
# The PRD-748 engine-cost row runs in `template-engine-cost`, beside this leg.
scenario_list=$(node "$GITHUB_WORKSPACE/scripts/non-visual-scenarios.mjs" "$target")
board=()
if [ "${{ github.event_name }}" != schedule ]; then board=(--board "${{ matrix.template }}"); fi
scenario_list=$(node "$GITHUB_WORKSPACE/scripts/non-visual-scenarios.mjs" "$target" "${board[@]}")
mapfile -t scenarios <<< "$scenario_list"
test "${#scenarios[@]}" -gt 0
echo "${{ matrix.template }}: ${#scenarios[@]} nonvisual scenarios"
Expand All @@ -948,13 +953,14 @@ jobs:
# 4x4 tile means (scripts/engine-parity.ts). Both arms share this runner's adapter, so a
# software rasteriser still catches a wrong exposure, colour, fog or a missing object.
- name: Compare the two engines on one pose
if: matrix.template != 'starter' || github.event_name == 'schedule'
run: >-
pnpm exec tsx scripts/engine-parity.ts
--project "$RUNNER_TEMP/threenative-${{ matrix.template }}"
--template ${{ matrix.template }}
--out artifacts/engine-parity/${{ matrix.template }}
- name: Retain the engine parity frames
if: always()
if: always() && (matrix.template != 'starter' || github.event_name == 'schedule')
uses: actions/upload-artifact@v4
with:
name: engine-parity-${{ matrix.template }}-${{ github.run_id }}-${{ github.run_attempt }}
Expand All @@ -970,14 +976,14 @@ jobs:
pnpm --dir "$target" exec threenative build --target web
test -s "$target/dist/index.html"

# PRD-748's engine-cost contract row, every PR, per kit. It draws all 1260 ticks one by one for
# its four counter windows, and on hosted SwiftShader that alone took 14m51s (run 41170629638,
# adventure): inside a kit's 30-minute `template-nonvisual` leg it cancelled adventure and
# collectathon. It runs here in parallel, with the same scenario, bounds and ticks. A kit that
# opts out prints its reason and passes; `ci-required` gates this job like any other.
# PRD-748's engine-cost contract row, nightly only (owner, 2026-11-01), per kit. It draws all 1260
# ticks one by one for its four counter windows, and on hosted SwiftShader that alone took 14m51s
# (run 41170629638, adventure): inside a kit's 30-minute `template-nonvisual` leg it cancelled
# adventure and collectathon. It runs here in parallel, with the same scenario, bounds and ticks.
# A kit that opts out prints its reason and passes; `ci-required` gates the nightly's run.
template-engine-cost:
needs: [scope, build-artifacts]
if: ${{ (needs.scope.outputs.selection == 'full' || needs.scope.outputs.selection == 'template') }}
if: ${{ github.event_name == 'schedule' && (needs.scope.outputs.selection == 'full' || needs.scope.outputs.selection == 'template') }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.scope.outputs.plan).templateMatrix }}
Expand Down Expand Up @@ -1091,20 +1097,27 @@ jobs:
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/workspace-dist
if: github.event_name == 'schedule'
with:
# Compiled once by build-artifacts and downloaded rather than rebuilt: this job is
# ordered behind it above, so the upload always exists and the cache key never misses.
shared-artifact: workspace-packages
save-bundles: "false"
pack-archives: "false"
- run: pnpm tsx scripts/count-loc.ts --check
- run: pnpm --filter abyss-vanilla build
- run: pnpm --filter abyss-framework build
- run: pnpm exec vitest run scripts/__tests__/count-loc.spec.ts scripts/__tests__/score-blind.spec.ts
# Owner cut (2026-11-01): a board keeps `count-loc --check`; the rest runs on the nightly.
- if: github.event_name == 'schedule'
run: pnpm --filter abyss-vanilla build
- if: github.event_name == 'schedule'
run: pnpm --filter abyss-framework build
- if: github.event_name == 'schedule'
run: pnpm exec vitest run scripts/__tests__/count-loc.spec.ts scripts/__tests__/score-blind.spec.ts
- uses: ./.github/actions/playwright-chromium
if: github.event_name == 'schedule'
# `test:browser` is the test-browser job's gate; running it again here bought nothing but
# a second copy of the slowest step in the workflow.
- run: sh scripts/xvfb.sh pnpm exec playwright test --config=benchmark.playwright.config.ts
- if: github.event_name == 'schedule'
run: sh scripts/xvfb.sh pnpm exec playwright test --config=benchmark.playwright.config.ts

# visuals is NOT a job here any more. It captures frames from a WebGPU canvas, and this runner
# has no GPU — its first execution in the repository's history failed on
Expand Down Expand Up @@ -1424,8 +1437,9 @@ jobs:
ctest --test-dir build/native-engine-wasm -L native-engine --no-tests=error --output-on-failure

integration:
needs: scope
if: needs.scope.result == 'success' && fromJSON(needs.scope.outputs.plan).jobs.integration.required
needs: [scope, build-artifacts]
# `!cancelled()`: a skipped or failed build-artifacts only means each leg builds its own workspace.
if: ${{ !cancelled() && needs.scope.result == 'success' && fromJSON(needs.scope.outputs.plan).jobs.integration.required }}
permissions:
contents: read
actions: read
Expand All @@ -1435,6 +1449,7 @@ jobs:
candidate_sha: ${{ needs.scope.outputs.candidate_sha }}
runner_admission: ${{ needs.scope.outputs.runner_admission }}
base_sha: ${{ needs.scope.outputs.base_sha }}
workspace_artifact: ${{ needs.build-artifacts.result == 'success' && 'workspace-packages' || '' }}

supply-chain:
needs: scope
Expand Down
20 changes: 19 additions & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ on:
runner_admission:
type: string
default: ""
# This run's compiled workspace from `build-artifacts`; empty builds it in the job.
workspace_artifact:
type: string
default: ""
base_sha:
type: string
default: ""
Expand Down Expand Up @@ -92,7 +96,7 @@ jobs:
# Exact source filters; workflow-only changes compare each job and filter separately.
TN_LANE_FILTERS: |
animation ^packages/core/src/animation\.ts$|^packages/core/__tests__/animation[^/]*\.spec\.ts$|^examples/abyss-framework/src/render/animation-reversal[^/]*\.ts$|^examples/abyss-framework/src/animation-reversal-main\.ts$|^examples/abyss-framework/animation-reversal\.html$|^examples/abyss-framework/playtests/animation-reversal\.playtest\.json$|^scripts/verify-animation-reversal\.ts$|^packages/core/__tests__/vq-locomotion-blend-spaces\.spec\.ts$|^examples/abyss-framework/src/render/locomotion-[^/]*\.ts$|^examples/abyss-framework/src/render/vq-locomotion-[^/]*\.ts$|^examples/abyss-framework/src/vq-locomotion-main\.ts$|^examples/abyss-framework/playtests/vq-locomotion-blend-spaces\.playtest\.json$|^examples/abyss-framework/assets/mannequin\.glb$|^scripts/verify-vq-locomotion\.ts$
cold-boot ^packages/create-threenative/(template-assets|templates/[^/]+/src/render)/(autoExposure|exposure|exposureGraph|exposureReadback|worldEnvironment)\.ts$|^packages/create-threenative/__tests__/auto-exposure[^/]*\.spec\.ts$|^packages/create-threenative/__tests__/fixtures/auto-exposure/.*$|^packages/core/src/.*$|^packages/core/patches/.*$|^packages/playtest/.*$|^packages/runtime-native/.*$|^pnpm-lock\.yaml$|^scripts/xvfb\.sh$|^\.github/actions/(workspace-dist|pnpm|playwright-chromium)/.*$
cold-boot ^packages/create-threenative/(template-assets|templates/[^/]+/src/render)/(autoExposure|exposure|exposureGraph|exposureReadback|worldEnvironment)\.ts$|^packages/create-threenative/__tests__/auto-exposure[^/]*\.spec\.ts$|^packages/create-threenative/__tests__/fixtures/auto-exposure/.*$|^packages/core/src/(.*/)?[^/]*[Ee]xposure[^/]*$
exposure ^packages/create-threenative/(template-assets|templates/[^/]+/src/render)/(autoExposure|exposure|exposureGraph|exposureReadback|worldEnvironment)\.ts$|^packages/create-threenative/__tests__/auto-exposure[^/]*\.spec\.ts$|^packages/create-threenative/__tests__/fixtures/auto-exposure/.*$|^packages/core/src/.*$|^packages/core/patches/.*$|^packages/playtest/.*$|^packages/runtime-native/.*$|^pnpm-lock\.yaml$|^scripts/xvfb\.sh$|^\.github/actions/(workspace-dist|pnpm|playwright-chromium)/.*$
csg ^examples/integrations/csg/.*$
decals ^packages/playtest/.*$|^packages/create-threenative/templates/shooter/src/render/decals\.ts$|^packages/create-threenative/templates/shooter/src/scenes/Play\.ts$|^packages/create-threenative/__tests__/shooter-decals[^/]*\.spec\.ts$|^packages/create-threenative/__tests__/fixtures/bounded-decals/.*$|^packages/runtime-native/.*$|^scripts/xvfb\.sh$|^\.github/actions/workspace-dist/.*$|^\.github/actions/pnpm/.*$|^\.github/actions/playwright-chromium/.*$|^pnpm-lock\.yaml$
Expand Down Expand Up @@ -230,6 +234,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
pack-archives: 'false'
save-bundles: 'false'
- uses: ./.github/actions/playwright-chromium
Expand Down Expand Up @@ -281,6 +286,7 @@ jobs:
- name: Build this source's public JavaScript packages
uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
save-bundles: 'false'
pack-archives: 'false'
- name: Verify native preflight and capture rejection controls
Expand All @@ -298,6 +304,7 @@ jobs:
libxcursor-dev libxi-dev libxfixes-dev libxss-dev libxkbcommon-dev
libgtk-3-dev libwebkit2gtk-4.1-dev libxcomposite-dev xcompmgr
libvulkan1 mesa-vulkan-drivers vulkan-tools xvfb zlib1g-dev
- uses: ./.github/actions/native-rust-cache
- name: Build the ordinary ARM desktop host
run: pnpm --filter @threenative/runtime-native native:build
- name: Run the same packaged decal scenarios and unchanged pixel gates
Expand Down Expand Up @@ -488,6 +495,7 @@ jobs:
- uses: ./.github/actions/native-engine-wasm
- uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
pack-archives: 'false'
save-bundles: 'false'
- uses: ./.github/actions/playwright-chromium
Expand Down Expand Up @@ -545,6 +553,7 @@ jobs:
- name: Build this source's public JavaScript packages
uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
save-bundles: 'false'
pack-archives: 'false'
- name: Restore checksum-provisioned native dependencies
Expand All @@ -560,6 +569,7 @@ jobs:
libxcursor-dev libxi-dev libxfixes-dev libxss-dev libxkbcommon-dev
libgtk-3-dev libwebkit2gtk-4.1-dev libxcomposite-dev xcompmgr
libvulkan1 mesa-vulkan-drivers vulkan-tools xvfb zlib1g-dev
- uses: ./.github/actions/native-rust-cache
- name: Build the ordinary ARM desktop host from this source
run: pnpm --filter @threenative/runtime-native native:build
- name: Bundle the authored locomotion entry for the native host
Expand Down Expand Up @@ -661,6 +671,7 @@ jobs:
- name: Build this source's public JavaScript packages
uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
save-bundles: 'false'
pack-archives: 'false'
- name: Verify native preflight and capture rejection controls
Expand All @@ -678,6 +689,7 @@ jobs:
libxcursor-dev libxi-dev libxfixes-dev libxss-dev libxkbcommon-dev
libgtk-3-dev libwebkit2gtk-4.1-dev libxcomposite-dev xcompmgr
libvulkan1 mesa-vulkan-drivers vulkan-tools xvfb zlib1g-dev
- uses: ./.github/actions/native-rust-cache
- name: Build the ordinary ARM desktop host
run: pnpm --filter @threenative/runtime-native native:build
- name: Verify the compiled surface acquisition status contract
Expand Down Expand Up @@ -852,6 +864,7 @@ jobs:
- name: Build this source's public JavaScript packages
uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
save-bundles: 'false'
pack-archives: 'false'
- name: Verify native preflight and capture rejection controls
Expand All @@ -869,6 +882,7 @@ jobs:
libxcursor-dev libxi-dev libxfixes-dev libxss-dev libxkbcommon-dev
libgtk-3-dev libwebkit2gtk-4.1-dev libxcomposite-dev xcompmgr
libvulkan1 mesa-vulkan-drivers vulkan-tools xvfb zlib1g-dev
- uses: ./.github/actions/native-rust-cache
- name: Build the ordinary ARM desktop host from this source
run: pnpm --filter @threenative/runtime-native native:build
- name: Run the same authored four-arm proof and missing-gate control
Expand Down Expand Up @@ -930,6 +944,7 @@ jobs:
- name: Build this commit's JavaScript packages
uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
save-bundles: 'false'
pack-archives: 'false'
- name: Validate real compressed-source normalization and capture guards
Expand All @@ -951,6 +966,7 @@ jobs:
libxcursor-dev libxi-dev libxfixes-dev libxss-dev libxkbcommon-dev
libgtk-3-dev libwebkit2gtk-4.1-dev libxcomposite-dev xcompmgr
libvulkan1 mesa-vulkan-drivers vulkan-tools xvfb zlib1g-dev
- uses: ./.github/actions/native-rust-cache
- name: Build the QuickJS host required by the packaged fallback
run: |
pnpm --filter @threenative/runtime-native native:build
Expand Down Expand Up @@ -1002,6 +1018,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
pack-archives: 'false'
save-bundles: 'false'
- uses: ./.github/actions/playwright-chromium
Expand Down Expand Up @@ -1046,6 +1063,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/workspace-dist
with:
shared-artifact: ${{ inputs.workspace_artifact }}
pack-archives: 'false'
save-bundles: 'false'
- uses: ./.github/actions/playwright-chromium
Expand Down
Loading
Loading