open-standards/.github/workflows/wai-studio.yml
Transaction Science 213c6aee6c wai doctor: ends what an answered program leaves running; CI checks each archive
Linux and macOS: a program asked is now waited for with waitid and
WNOWAIT, which leaves it unreaped. Once it has exited, its process
group gets SIGKILL, and only then is it reaped. While the leader is an
unreaped zombie, its group's number cannot be given to another group.
So this ends what the program left running behind it, whether or not
it answered, and nothing else. A program that does not answer in time
is stopped with its group before it is reaped, as before. Other Unix
systems keep the polling wait. On Windows the job's kill-on-close does
the same, and the new test runs there too.

A program file that is open for writing somewhere cannot be run for a
moment (ETXTBSY), for example one just written while another thread of
this process starts a program. A spawn refused so is now tried again
for up to 200 ms. This was a real flake in the tests that write
stand-in programs, and wai doctor itself asks several programs at once.

CI: wai/quickstart/test/archive_doctor.py runs bin/wai doctor --json
from an unpacked archive, with the archive's bin first on PATH. It
fails unless every install and stack check passes and each piece those
checks found is inside the archive. Without that, a relay elsewhere on
PATH or Studio's files from WAI_STUDIO would hide a piece the archive
lacks. Ports, browsers and the keystore are printed, not required.
stack.web and stack.studio now name their path in facts.

The release runs it on every leg that builds an archive: macOS where
the triple is the host's, Linux x86_64 and arm64, and Windows on the
staged directory. At PR time it runs on the macOS archive wai-examples
builds, the only archive built then. archive_doctor_test.py runs it
against stand-ins in wai-examples' portable job.

Real browsers on Linux: wai-studio's webkit job runs in the browser
image and builds wai. It now requires browser.conformance to pass with
that wai.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-06 17:24:17 -05:00

199 lines
8.9 KiB
YAML

name: WAI Studio
# WAI Studio on the web (wai/wai-studio): one front end, its core in WebAssembly, served by
# `wai dev` at /_wai/ and hosted by the WAI site.
#
# - catalogue · linux: Studio's actions hold to the operation catalogue (scripts/actions.mjs
# --check: an action with no operation, an operation placed for the browser with no action,
# a mismatched name), the unit tests that need no core build (the generation and every rule
# of the drift check broken in a copy, the palette, the local core's calls), and the core's
# own tests and clippy (core/, wai-studio-core, on the http-conformance vectors);
# - browser · macos: e2e/run.sh builds wai, the relay, the room service, the core, the room
# client's packages, the quantum engine, Quantum Studio and Studio; runs every unit test,
# the core's calls among them, and the drift check; then e2e/studio.e2e.mjs against a
# `wai dev` stack in Chromium and Firefox: each panel on both hosts, Inspect and Verify on
# conformance vectors with the results their cases expect, a room joined as a viewer, the
# palette, axe (WCAG 2.2 A and AA), no console error or failed request;
# - webkit · linux: the same in WebKit, in the browser image of the Playwright release
# package-lock.json pins (by digest). The macOS runner is started from SSH, and a process of
# an SSH session there cannot open WebKit's helper processes: WebKit never launches. Then
# `wai doctor --json` there must pass browser.conformance: it finds the browsers the image
# holds, and its chromium runs (wai/quickstart/test/archive_doctor.py --require).
#
# `wai dev`'s side (/_wai/ on its origin, nothing listening beyond loopback) is tested in the
# WAI conformance workflow's quickstart step (tests/cli_studio.rs and the binary's own tests).
#
# Push filters match the push's delta, so every path Studio builds on is listed.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'wai/wai-studio/**'
- 'wai/wai-rs/**'
- 'wai/catalogue/**'
- 'wai/quantum-studio/**'
- 'wai/wai-web/**'
- 'wai/wai-quantum/**'
- 'wai/wai-ops/**'
- 'wai/wai-live-web/**'
- 'wai/quickstart/build-web.sh'
- 'wai/quickstart/test/archive_doctor.py'
- 'wai/http-conformance/**'
- 'wai/pack-conformance/stills/**'
- 'joule-code/joule-code-rs/crates/joule-moq-web/**'
- 'joule-code/joule-code-rs/crates/joule-moq-relay/**'
- 'joule-code/joule-code-rs/crates/joule-moq-room/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-studio.yml'
pull_request:
paths:
- 'wai/wai-studio/**'
- 'wai/wai-rs/**'
- 'wai/catalogue/**'
- 'wai/quantum-studio/**'
- 'wai/wai-web/**'
- 'wai/wai-quantum/**'
- 'wai/wai-ops/**'
- 'wai/wai-live-web/**'
- 'wai/quickstart/build-web.sh'
- 'wai/quickstart/test/archive_doctor.py'
- 'wai/http-conformance/**'
- 'wai/pack-conformance/stills/**'
- 'joule-code/joule-code-rs/crates/joule-moq-web/**'
- 'joule-code/joule-code-rs/crates/joule-moq-relay/**'
- 'joule-code/joule-code-rs/crates/joule-moq-room/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-studio.yml'
jobs:
# The path filters gate the trigger; this job gates the work from the pull request's own
# diff (scripts/pr-touches-paths.py, reading the list above), as wai-pack.yml explains.
relevant:
name: does this change touch it
runs-on: gate
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-studio.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches WAI Studio: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
catalogue:
name: the catalogue's actions, the unit tests and the core · linux
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- name: Install Rust 1.99.1
shell: bash
run: |
export PATH="$HOME/.cargo/bin:$PATH"
command -v rustup >/dev/null 2>&1 || \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.99.1
rustup toolchain install 1.99.1 --profile minimal --component clippy
rustup default 1.99.1
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Studio's actions hold to the catalogue
working-directory: wai/wai-studio
run: node scripts/actions.mjs --check
# The core's calls (test/core.test.mjs) need the core built for the web; the browser
# job runs them. These need nothing built.
- name: Unit tests
shell: bash
working-directory: wai/wai-studio
run: |
set -euo pipefail
node --test --test-reporter=tap test/actions.test.mjs test/palette.test.mjs test/local-core.test.mjs 2>&1 | tee "$RUNNER_TEMP/unit.log"
grep -qE '^# pass [1-9]' "$RUNNER_TEMP/unit.log" && grep -qE '^# fail 0$' "$RUNNER_TEMP/unit.log" || { echo "::error::no unit test ran, or one failed"; exit 1; }
- name: The core's tests and clippy
shell: bash
working-directory: wai/wai-studio/core
run: |
set -euo pipefail
cargo test --locked 2>&1 | tee "$RUNNER_TEMP/core.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/core.log" || { echo "::error::no test ran"; exit 1; }
cargo clippy --locked --all-targets -- -D warnings
browser:
name: Studio in Chromium and Firefox · macos
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: macos
timeout-minutes: 120
steps:
- uses: actions/checkout@v5
# launchd's PATH omits cargo and node on the macOS host runner.
- name: Put cargo and node on PATH
shell: bash
run: |
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
echo "/opt/homebrew/bin" >> "$GITHUB_PATH"
- name: wasm32 target and wasm-bindgen CLI 0.2.127
shell: bash
run: |
rustup target add wasm32-unknown-unknown
command -v wasm-bindgen >/dev/null && wasm-bindgen --version | grep -q ' 0\.2\.127$' || cargo install --locked wasm-bindgen-cli --version 0.2.127
- name: The browsers Playwright drives
shell: bash
working-directory: wai/wai-studio
run: |
set -euo pipefail
npm ci --ignore-scripts --no-audit --no-fund
npx playwright install chromium firefox
- name: Build everything, the unit tests, the drift check, and Studio in Chromium and Firefox
shell: bash
env:
STUDIO_OUT: ${{ runner.temp }}/wai-studio-e2e
run: nice -n 10 bash wai/wai-studio/e2e/run.sh --browsers chromium,firefox
webkit:
name: Studio in WebKit · linux
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 150
# Playwright 1.60.0's image (package-lock.json pins 1.60.0): Node, and WebKit with every
# library it needs.
container:
image: mcr.microsoft.com/playwright@sha256:9bd26ad900bb5e0f4dee75839e957a89ae89c2b7ab1e76050e559790e946b948
steps:
- uses: actions/checkout@v5
# The floor's zstd is C, compiled for wasm32 by clang and archived by llvm-ar.
- name: Rust 1.99.1 with the wasm32 target, clang and llvm, wasm-bindgen CLI 0.2.127
shell: bash
run: |
set -euo pipefail
apt-get update -qq
apt-get install -y -qq --no-install-recommends curl ca-certificates build-essential pkg-config clang llvm python3 git > /dev/null
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.99.1
"$HOME/.cargo/bin/rustup" target add wasm32-unknown-unknown
"$HOME/.cargo/bin/cargo" install --locked wasm-bindgen-cli --version 0.2.127
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Build everything, the unit tests, the drift check, and Studio in WebKit
shell: bash
env:
STUDIO_OUT: ${{ runner.temp }}/wai-studio-e2e
run: bash wai/wai-studio/e2e/run.sh --browsers webkit
# The wai run.sh built (its target directory: target-studio), in this image, finds the
# automation package's browsers (PLAYWRIGHT_BROWSERS_PATH) and its chromium runs.
- name: wai doctor finds the image's browsers, and its chromium runs
shell: bash
run: python3 -I wai/quickstart/test/archive_doctor.py --wai target-studio/wai-rs/release/wai --require browser.conformance