name: joule-moq-web browser client # The browser MoQ client (joule-code/joule-code-rs/crates/joule-moq-web) against # joule-moq-relay on loopback, in the host's installed browser, driven by the automation # package of e2e/package.json, with a fake camera and microphone # (crates/joule-moq-web/e2e/run.sh): # # compose live-conformance/compose in the page: every layout, mix, descriptor and # composition case through wai-live-web's wasm build, and wai-rs's own # corpus check run in the browser; # webtransport two participants and the room's clock over WebTransport: media both ways, # every object verified against its receipt (the relay's door checks each # too), the composed canvas's tiles equal to the host's layout for the same # descriptor and set, the mix in an AudioWorklet, captions with their origin # label, and a late joiner served earlier groups by FETCH; # websocket the same over the WebSocket binding, forced; # goaway the relay shut down with GOAWAY naming a second: every page moves and # closes its old session before the timeout; # service a room service (joule-moq-room) keeps the room, as in production: pages # make their own keys and enter from their buttons, the service grants and # its clock authors the lifecycle, the relay (WebSocket only, behind a # same-origin proxy) honours the grants by the service's authority key; # e2ee an end-to-end encrypted room (WAI confidentiality) kept by a room service: # pages key through the service's log, seal every frame and open every object # before it is decoded; a viewer outside the group opens nothing, every change # a relay could make is refused with its reason, a late joiner opens nothing # sealed before its join, a member that stops publishing is removed and opens # no later epoch, and the relay's DVR store keeps nothing of the room. # # The macos runner is the host with the browser installed; the tests drive that browser # (its executable named by BROWSER_BIN), not a bundled one. The automation package comes # from e2e/package-lock.json with install scripts off. Each browser runs under the host's # memguard where it exists. Nothing listens beyond loopback. # # Push filters match the push's delta, so every path the job reads is listed. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: push: branches: [main] paths: - 'joule-code/joule-code-rs/crates/joule-moq-web/**' - 'joule-code/joule-code-rs/crates/joule-moq/**' - 'joule-code/joule-code-rs/crates/joule-moq-mls/**' - 'joule-code/joule-code-rs/crates/joule-moq-relay/**' - 'joule-code/joule-code-rs/crates/joule-moq-room/**' - 'joule-code/joule-code-rs/crates/jwp/**' - 'joule-code/joule-code-rs/Cargo.toml' - 'joule-code/joule-code-rs/Cargo.lock' - 'joule-code/joule-code-rs/vendor/**' - 'wai/wai-live-web/**' - 'wai/wai-rs/**' - 'wai/wai-ops/**' - 'wai/live-conformance/compose/**' - '.github/workflows/joule-moq-web.yml' pull_request: paths: - 'joule-code/joule-code-rs/crates/joule-moq-web/**' - 'joule-code/joule-code-rs/crates/joule-moq/**' - 'joule-code/joule-code-rs/crates/joule-moq-mls/**' - 'joule-code/joule-code-rs/crates/joule-moq-relay/**' - 'joule-code/joule-code-rs/crates/joule-moq-room/**' - 'joule-code/joule-code-rs/crates/jwp/**' - 'joule-code/joule-code-rs/Cargo.toml' - 'joule-code/joule-code-rs/Cargo.lock' - 'joule-code/joule-code-rs/vendor/**' - 'wai/wai-live-web/**' - 'wai/wai-rs/**' - 'wai/wai-ops/**' - 'wai/live-conformance/compose/**' - '.github/workflows/joule-moq-web.yml' jobs: # Forgejo matches `paths:` against the push delta, so a rebase fires this workflow # whatever the pull request changes. This job reads the pull request's own diff against # the paths above (scripts/pr-touches-paths.py, which fails open). relevant: name: joule-moq-web · does this change touch it 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/joule-moq-web.yml \ "${{ github.event.pull_request.base.sha }}" \ "${{ github.event.pull_request.head.sha }}") echo "this change touches the browser client: $r" echo "run=$r" >> "$GITHUB_OUTPUT" e2e: name: joule-moq-web · browser end-to-end (macos) needs: relevant if: needs.relevant.outputs.run == 'true' runs-on: macos timeout-minutes: 90 steps: - uses: actions/checkout@v5 # launchd's PATH omits cargo on the macOS host runner. - name: Put cargo on PATH shell: bash run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" # Pinned to the wasm-bindgen both crates pin: the CLI and the crate must match. - name: wasm32 target and wasm-bindgen CLI 0.2.127 shell: bash run: | set -euo pipefail rustup target add wasm32-unknown-unknown cargo install --locked wasm-bindgen-cli --version 0.2.127 # The page's codec is joule-moq built without its runtime; this is the build that # holds the feature split. - name: joule-moq without its runtime shell: bash working-directory: joule-code/joule-code-rs run: nice -n 10 cargo check --locked -p joule-moq --no-default-features - name: The browser end-to-end tests shell: bash env: BROWSER_BIN: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome MOQ_WEB_OUT: ${{ runner.temp }}/moq-web run: | set -euo pipefail test -x "$BROWSER_BIN" || { echo "::error::this runner has no browser at $BROWSER_BIN"; exit 1; } mg=/Volumes/macos_4TB_external_1/wai-build/bin/memguard if [ -x "$mg" ]; then export MEMGUARD="$mg"; fi nice -n 10 bash joule-code/joule-code-rs/crates/joule-moq-web/e2e/run.sh 2>&1 | tee "$RUNNER_TEMP/moq-web-e2e.log" grep -qE '^room e2e: 1 of 1 runs passed; ' "$RUNNER_TEMP/moq-web-e2e.log" \ || { echo "::error::the browser end-to-end run did not pass"; exit 1; } if grep -q '^FAIL' "$RUNNER_TEMP/moq-web-e2e.log"; then echo "::error::a check failed"; exit 1; fi