name: WAI examples # Every example in wai/examples/ run as a person runs it: `wai create` makes the project from # the template the binary holds, and `wai dev` runs it (wai/quickstart/test/run-examples.sh). # Each fails the job if its example stops working. # # - portable (linux): remote-desktop (a drawn screen shared into a room, the session accepted # by the independent verifier), package-and-deliver (packed and verified), verify-receipts # (the Rust verifier on this checkout's wai crate and the Python one, each refusing a changed # byte), encoder-ingest (SRT into a room, every group verified against its receipt); and the # package managers' entries the release writes (wai/quickstart/test/formulae_test.py): every # binary of the archive but wai-quantum, which they depend on as its own package, and that # one built as its own release builds it; and the check of `wai doctor` from an archive # (wai/quickstart/test/archive_doctor_test.py), against stand-ins. # - browser (macos): the room client and WAI Studio built and packaged as the release packages # them (wai/quickstart/build-web.sh, build-studio.sh, build.sh, its bin/ with wai-ingest and # wai-quantum), the checkout left as it was; the quickstart's readiness check, `wai dev --check`, # from the unpacked archive in an empty directory, Studio at /_wai/ among the pieces it checks; # `wai doctor --json` from the unpacked archive, every install and stack check passed # (wai/quickstart/test/archive_doctor.py; the release runs it on every platform's archive); # the quickstart on a clean machine (wai/quickstart/test/clean-machine.sh: a fresh HOME and # directory, the system's PATH, every binary and Studio's files in the archive, each binary # run, then hello, verify, create and dev --check); then video-room, watch-together, # messaging, remote-desktop and package-and-deliver in the host's browser; and apple-app # (the native sink's macOS slice, the SwiftUI app built, a recorded session replayed). # - android (linux-arm64): android-app, the native sink's Kotlin API on the host JVM. # # Push filters match the push's delta, so every path an example builds on is listed. concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }} cancel-in-progress: true on: pull_request: paths: - 'wai/examples/**' - 'wai/quickstart/**' - 'wai/wai-rs/**' - 'wai/wai-pack/**' - 'wai/wai-live-web/**' - 'wai/wai-quantum/**' - 'wai/wai-quantum-cli/**' - 'wai/wai-quantum-qpu/**' - 'wai/wai-studio/**' - 'wai/quantum-studio/**' - 'wai/wai-web/**' - 'wai/wai-ops/**' - 'wai/wai-sink-swift/**' - 'wai/wai-sink-android/**' - 'wai/claim-conformance/_ed25519.py' - 'wai/claim-conformance/_blake3.py' - 'wai/live-conformance/remote_desktop_verify.py' - 'wai/live-conformance/live_rooms_verify.py' - 'wai/live-conformance/telemetry_aggregate_verify.py' - 'wai/pack-conformance/packs/video-base-01/**' - 'joule-code/joule-code-rs/**' # Read by wai/quickstart/test/formulae_test.py. - '.github/workflows/wai-release.yml' - '.github/workflows/wai-quantum-cli-release.yml' - '.github/workflows/wai-examples.yml' schedule: - cron: '17 5 * * *' 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: WAI examples · does this change touch them runs-on: linux 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-examples.yml \ "${{ github.event.pull_request.base.sha }}" \ "${{ github.event.pull_request.head.sha }}") echo "this change touches the examples: $r" echo "run=$r" >> "$GITHUB_OUTPUT" portable: name: WAI examples · portable · linux needs: relevant if: needs.relevant.outputs.run == 'true' runs-on: linux timeout-minutes: 90 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 rustup default 1.99.1 SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo" command -v python3 >/dev/null && command -v curl >/dev/null || { $SUDO apt-get update -qq; $SUDO apt-get install -y -qq python3 curl; } echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" # The binaries the archive holds, from this checkout. The portable examples load no page, # so the room client's files are the source tree's (WAI_WEB), without its WebAssembly. - name: Build wai, the relay, the room service, the remote host and the packager shell: bash run: | set -euo pipefail b="$RUNNER_TEMP/bin"; mkdir -p "$b" out() { (cd "$1" && cargo metadata --format-version 1 --no-deps --locked | python3 -c 'import json,sys; print(json.load(sys.stdin)["target_directory"])'); } (cd wai/wai-rs && cargo build --locked --release --no-default-features --features quickstart --bin wai) && cp "$(out wai/wai-rs)/release/wai" "$b/" (cd joule-code/joule-code-rs && cargo build --locked --release -p joule-moq-relay --bin wai-relay) && cp "$(out joule-code/joule-code-rs)/release/wai-relay" "$b/" (cd joule-code/joule-code-rs/crates/joule-moq-room && cargo build --locked --release --bin wai-room) && cp "$(out joule-code/joule-code-rs/crates/joule-moq-room)/release/wai-room" "$b/" (cd joule-code/joule-code-rs/crates/joule-moq-remote && cargo build --locked --release --bin wai-remote-host) && cp "$(out joule-code/joule-code-rs/crates/joule-moq-remote)/release/wai-remote-host" "$b/" (cd wai/wai-pack && cargo build --locked --release --bin wai-pack) && cp "$(out wai/wai-pack)/release/wai-pack" "$b/" echo "$b" >> "$GITHUB_PATH" - name: The portable examples shell: bash env: WAI_WEB: ${{ github.workspace }}/joule-code/joule-code-rs/crates/joule-moq-web/web run: bash wai/quickstart/test/run-examples.sh portable - name: The Homebrew formula and the Scoop manifest the release writes shell: bash run: python3 -I wai/quickstart/test/formulae_test.py - name: The check of wai doctor from an archive, against stand-ins shell: bash run: python3 -I wai/quickstart/test/archive_doctor_test.py browser: name: WAI examples · the archive, the readiness check, the browser and Apple · 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 room client, WAI Studio and the archive, as the release builds them shell: bash run: | set -euo pipefail nice -n 10 bash wai/quickstart/build-web.sh "$RUNNER_TEMP/web" nice -n 10 bash wai/quickstart/build-studio.sh "$RUNNER_TEMP/studio" nice -n 10 bash wai/quickstart/build.sh "$RUNNER_TEMP/dist" --web "$RUNNER_TEMP/web" --studio "$RUNNER_TEMP/studio" # The three builds leave the checkout as they found it: everything they write is under # an ignored directory or outside the tree. left=$(git status --porcelain --untracked-files=all) [ -z "$left" ] || { echo "$left"; echo "::error::the builds left files in the checkout"; exit 1; } mkdir -p "$RUNNER_TEMP/unpacked" tar -C "$RUNNER_TEMP/unpacked" -xzf "$RUNNER_TEMP"/dist/wai-*.tar.gz echo "WAI_DIST=$(ls -d "$RUNNER_TEMP"/unpacked/wai-*)" >> "$GITHUB_ENV" # The quickstart's readiness check: from the archive alone, in an empty directory, the # relay, the room service and the page on one origin, each piece checked through it. - name: wai hello, wai verify and wai dev --check from the archive shell: bash run: | set -euo pipefail d="$RUNNER_TEMP/clean"; rm -rf "$d"; mkdir -p "$d"; cd "$d" "$WAI_DIST/bin/wai" hello h "$WAI_DIST/bin/wai" verify h mkdir p && cd p && "$WAI_DIST/bin/wai" dev --check --port 0 | tee "$RUNNER_TEMP/dev-check.out" grep -q "ok WAI Studio at /_wai/" "$RUNNER_TEMP/dev-check.out" || { echo "::error::the archive's wai dev does not serve WAI Studio"; exit 1; } # wai doctor from the unpacked archive, its bin first on PATH: every install and stack check # passes; the ports and the browsers depend on the runner, and are printed. - name: wai doctor from the archive shell: bash run: python3 -I wai/quickstart/test/archive_doctor.py "$WAI_DIST" # The quickstart as a new user runs it, from the archive and its .sha256 alone, with no # browser step: it fails when the archive lacks any binary, the room client, or Studio's # page and core, or when a step fails. - name: The quickstart on a clean machine, from the archive shell: bash run: bash wai/quickstart/test/clean-machine.sh "$RUNNER_TEMP"/dist/wai-*.tar.gz - name: The examples in the browser shell: bash env: BROWSER_BIN: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome # allow-name: the macOS runner's browser executable, a file path run: | set -euo pipefail test -x "$BROWSER_BIN" || { echo "::error::this runner has no browser at $BROWSER_BIN"; exit 1; } nice -n 10 bash wai/quickstart/test/run-examples.sh browser # The apple example builds WaiFFI.xcframework, which cross-compiles each # slice (wai-sink-swift/scripts/build-xcframework.sh names the set in its # header). A target's std belongs to one toolchain, so the pin moving to # 1.99.1 left this job with none of them, and the macos slice failed at # x86_64-apple-darwin with E0463 "can't find crate for `core`". The step # had been passing on targets a previous toolchain happened to have # installed on the runner; naming them here is what makes it reproducible # on a host that has never built this. The tvos slices' nightly is a # separate toolchain and keeps its own targets, so it is not re-added. - name: The targets the apple slices need shell: bash run: | set -euo pipefail rustup target add aarch64-apple-darwin x86_64-apple-darwin aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios - name: apple-app shell: bash run: nice -n 10 bash wai/quickstart/test/run-examples.sh apple android: name: WAI examples · android-app · linux-arm64 needs: relevant if: needs.relevant.outputs.run == 'true' runs-on: linux-arm64 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 rustup default 1.99.1 echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" - name: JDK 17 shell: bash run: | set -euo pipefail j="/usr/lib/jvm/java-17-openjdk-$(dpkg --print-architecture)" if [ ! -x "$j/bin/javac" ]; then sudo=$(command -v sudo || true) $sudo apt-get update -qq $sudo apt-get install -y -qq --no-install-recommends openjdk-17-jdk-headless >/dev/null fi echo "JAVA_HOME=$j" >> "$GITHUB_ENV" - name: wai shell: bash run: | set -euo pipefail (cd wai/wai-rs && cargo build --locked --release --no-default-features --features quickstart --bin wai) t=$(cd wai/wai-rs && cargo metadata --format-version 1 --no-deps --locked | python3 -c 'import json,sys; print(json.load(sys.stdin)["target_directory"])') echo "$t/release" >> "$GITHUB_PATH" - name: android-app shell: bash run: | set -euo pipefail cache=/ci-cache/wai-sink-android mkdir -p "$cache" 2>/dev/null || cache="$RUNNER_TEMP/wai-sink-android" export WAI_SINK_GRADLE_DISTS="$cache/dists" GRADLE_USER_HOME="$cache/gradle-home" bash wai/quickstart/test/run-examples.sh android