ci(perf): add the weekly baseline tightening workflow

Plan item B4. performance-ratchet.yml measures master on three runners
(initial bytes and the journeys) and tighten-baselines.mjs lowers every
baseline that all three runs beat, opening a no-release-note PR only when
the file changed. The journeys steps move into a composite action shared
with ci.yml's Performance journeys job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-09-29 22:52:13 +02:00
1 parent 963a431bb7
commit a7f1ecfe49
9 files changed
+1110 -58

No files matched your search

@@ -0,0 +1,74 @@
name: Run the performance journeys
description: >-
Runs `pnpm run perf:journeys` under xvfb on an Ubuntu runner, writes the
measurements to the job summary and returns the path of the summary.json
it wrote. Callers check out, set up pnpm and Node, install dependencies
and upload what they need. Contract: docs/architecture/performance-journeys.md.
outputs:
summary:
description: Path of the journey summary.json this run wrote.
value: ${{ steps.summary.outputs.path }}
runs:
using: composite
steps:
# The journeys drive Electron through Playwright's _electron API
# and never launch a Playwright browser, so no `playwright
# install`. The runner image ships Electron's shared libraries and
# xvfb; fail fast with a clear message if an image update drops one.
# pnpm skips Electron's postinstall, so download the binary first;
# otherwise ldd sees no file and the check passes vacuously.
- name: Check Electron runtime dependencies
shell: bash
run: |
command -v xvfb-run || { echo "::error::xvfb-run is missing on the runner"; exit 1; }
node tools/testing/ensure-electron-binary.mjs || { echo "::error::Electron binary download failed"; exit 1; }
missing="$(ldd node_modules/electron/dist/electron | grep 'not found' || true)"
if [ -n "$missing" ]; then
echo "::error::Electron is missing shared libraries:"
echo "$missing"
exit 1
fi
# The Nx target builds electron-backend:build-performance first
# and playwright.journeys.config.ts starts the Xtream mock server.
- name: Run the performance journeys
shell: bash
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" pnpm run perf:journeys
env:
CI: 'true'
NX_TASKS_RUNNER_DYNAMIC_OUTPUT: 'false'
# Every run writes a fresh timestamped directory, so a clean
# checkout must hold exactly one summary.
- name: Locate the journey summary
id: summary
shell: bash
run: |
set -euo pipefail
mapfile -t summaries < <(find dist/performance/journeys -mindepth 2 -maxdepth 2 -name summary.json)
if [ "${#summaries[@]}" -ne 1 ]; then
echo "::error::Expected one journey summary, found ${#summaries[@]}"
exit 1
fi
echo "path=${summaries[0]}" >> "$GITHUB_OUTPUT"
- name: Report journey measurements
shell: bash
env:
SUMMARY: ${{ steps.summary.outputs.path }}
run: |
set -euo pipefail
jq -r '
"## Performance journeys (\(.harness.platform), \(.harness.measuredIterations) measured iterations)", "",
(.journeys | to_entries[] | .key as $journey | .value as $j |
"### `\($journey)`", "",
"| Measurement | Value | Iterations |",
"| --- | ---: | --- |",
(($j.counters // {}) | to_entries[] |
($j.counterStability[.key] // {}) as $s |
"| `\(.key)` | \(.value) | \(($s.values // []) | map(tostring) | join(", "))\(if $s.stable == false then " (unstable)" else "" end) |"),
(($j.wallClock // {}) | to_entries[] | "| `\(.key)` | \(.value) | |"),
"")
' "$SUMMARY" | tee -a "$GITHUB_STEP_SUMMARY"