mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
the readiness loops fell through silently after 30 tries, so a server that never started surfaced as an opaque driver failure minutes later. each now says what did not answer and on which port.
148 lines
5.1 KiB
YAML
148 lines
5.1 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@v4
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v5
|
|
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@v1
|
|
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 }}
|
|
|
|
- name: Summarise
|
|
if: always()
|
|
run: |
|
|
{
|
|
echo "### replay-ui dogfood"
|
|
echo
|
|
echo "- seed \`$SEED\`, budget $MAX_STEPS steps"
|
|
for dir in runs/dogfood/*/; do
|
|
steps=$(wc -l < "$dir/trace.jsonl" | tr -d ' ')
|
|
violations=$(grep -c '"violations":\[' "$dir/trace.jsonl" || true)
|
|
echo "- $steps steps recorded, $violations step(s) with violations"
|
|
done
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
env:
|
|
SEED: ${{ inputs.seed }}
|
|
MAX_STEPS: ${{ inputs.max-steps }}
|
|
|
|
- name: Upload runs
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: replay-ui-runs
|
|
path: runs/
|
|
retention-days: 14
|