mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
checkout v4->v7, setup-go v5->v7, setup-node v4->v7, setup-java v4->v5, upload-artifact v4->v7, cache v4->v6, upload-pages-artifact v3->v5, deploy-pages v4->v5, setup-chrome v1->v2, setup-android v3->v4, goreleaser-action v6->v7. setup-bun and android-emulator-runner are already node24; buf-setup-action stays on its deliberate SHA pin. setup-chrome v2 resolves stable from Chrome for Testing rather than the official installer, so the ci.yml comment about the action's default no longer held.
143 lines
5.0 KiB
YAML
143 lines
5.0 KiB
YAML
name: replay-ui
|
|
|
|
# Sanderling fuzzing sanderling's own replay UI. Dispatch-only: it takes minutes
|
|
# and it is a demo of the product loop, not a merge gate.
|
|
#
|
|
# The shape is: produce a real trace, serve it with `sanderling replay`, then run
|
|
# a spec against that UI. Any violation fails the job. Six of the seven
|
|
# properties in replay-ui/sanderling/spec.ts are cross-panel agreements that hold
|
|
# for any trace; the seventh is the stock noUncaughtExceptions. None of them
|
|
# needs recalibrating when the fixture changes.
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
seed:
|
|
description: seed for the dogfood run
|
|
default: "3"
|
|
max-steps:
|
|
description: step budget for the dogfood run
|
|
default: "80"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
dogfood:
|
|
timeout-minutes: 45
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v7
|
|
with:
|
|
go-version-file: go.mod
|
|
cache: true
|
|
|
|
- name: Set up bun
|
|
uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3.13"
|
|
|
|
# Pinned stable plus the AppArmor sysctl: the same setup ci.yml's browser
|
|
# job needs to get headless Chrome up on ubuntu-latest.
|
|
- name: Set up Chrome
|
|
uses: browser-actions/setup-chrome@v2
|
|
with:
|
|
chrome-version: stable
|
|
|
|
- name: Allow Chrome under unprivileged user namespaces
|
|
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
|
|
|
- name: Verify headless Chrome starts
|
|
run: |
|
|
chrome --version
|
|
chrome --headless --no-sandbox --disable-gpu --disable-dev-shm-usage \
|
|
--dump-dom 'data:text/html,<title>ok</title>'
|
|
|
|
# The UI the spec drives is the one embedded in this binary, so the build
|
|
# has to come after any change to replay-ui/src.
|
|
- name: Build sanderling
|
|
run: make sanderling-web
|
|
|
|
# A trace with a violation and uncaught exceptions in it, so the UI has
|
|
# something to render in every panel the spec looks at. No
|
|
# --exit-on-violation here: the run is the fixture, and stopping it at the
|
|
# first violation would leave a four-step trace to fuzz.
|
|
- name: Record a fixture trace
|
|
run: |
|
|
python3 -m http.server 8792 --bind 127.0.0.1 \
|
|
--directory test/browser/testdata/throwing &
|
|
ready=""
|
|
for _ in $(seq 1 30); do
|
|
curl -sf http://127.0.0.1:8792/ >/dev/null && { ready=1; break; }
|
|
sleep 1
|
|
done
|
|
if [ -z "$ready" ]; then
|
|
echo "the fixture http server never answered on 127.0.0.1:8792" >&2
|
|
exit 1
|
|
fi
|
|
./bin/sanderling test \
|
|
--platform web \
|
|
--spec test/browser/testdata/throwing/spec.ts \
|
|
--bundle-id http://127.0.0.1:8792/ \
|
|
--duration 5m --max-steps 25 --seed 7 \
|
|
--output runs/fixture
|
|
|
|
- name: Serve the trace with sanderling replay
|
|
run: |
|
|
# Flags before the positional argument: Go's flag package stops
|
|
# parsing at the first non-flag word.
|
|
./bin/sanderling replay --port 8793 --no-open runs/fixture &
|
|
ready=""
|
|
for _ in $(seq 1 30); do
|
|
curl -sf http://127.0.0.1:8793/api/runs >/dev/null && { ready=1; break; }
|
|
sleep 1
|
|
done
|
|
if [ -z "$ready" ]; then
|
|
echo "sanderling replay never served /api/runs on 127.0.0.1:8793" >&2
|
|
exit 1
|
|
fi
|
|
run_id="$(ls runs/fixture | head -1)"
|
|
echo "RUN_URL=http://127.0.0.1:8793/runs/$run_id/steps/1" >> "$GITHUB_ENV"
|
|
curl -sf "http://127.0.0.1:8793/runs/$run_id/steps/1" >/dev/null
|
|
|
|
# Inputs go through env rather than into the script text: a `${{ }}` is
|
|
# substituted before bash ever sees the line, so a seed of `$(id)` would
|
|
# run as a command.
|
|
- name: Fuzz the replay UI
|
|
run: |
|
|
./bin/sanderling test \
|
|
--platform web \
|
|
--spec replay-ui/sanderling/spec.ts \
|
|
--bundle-id "$RUN_URL" \
|
|
--duration 10m \
|
|
--max-steps "$MAX_STEPS" \
|
|
--seed "$SEED" \
|
|
--exit-on-violation \
|
|
--output runs/dogfood
|
|
env:
|
|
SEED: ${{ inputs.seed }}
|
|
MAX_STEPS: ${{ inputs.max-steps }}
|
|
|
|
# Exit 0 above means no property returned false. It does not mean any
|
|
# property was ever evaluated against real content: they all decline to
|
|
# judge when the elements they read are absent, so a run that never
|
|
# rendered the step page is green and worthless. This step is what tells
|
|
# the two apart, and it fails the job when nothing was judged.
|
|
- name: Summarise
|
|
if: always()
|
|
run: .github/scripts/replay-ui-summary.sh runs/dogfood
|
|
env:
|
|
SEED: ${{ inputs.seed }}
|
|
MAX_STEPS: ${{ inputs.max-steps }}
|
|
|
|
- name: Upload runs
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: replay-ui-runs
|
|
path: runs/
|
|
retention-days: 14
|