Skip to content

ci: batch short e2e apps (+re-rightsize), prewarm e2e caches, retry flaky Playwright tests - #25669

Open
sentry-junior[bot] wants to merge 13 commits into
developfrom
ci/flaky-retries-and-batched-e2e
Open

sentry-junior[bot] wants to merge 13 commits into
developfrom
ci/flaky-retries-and-batched-e2e

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

i was able to cut CI down significantly with caching slow downloads, bounded retries (we download a bunch of stuff lol) to kill download flakiness, and best of all batched e2e jobs (+ a re-rightsizing)

no test coverage changes/reductions at all

here's the end result (compared to a few close commits (21f2cf1, 40eff80, f15e256, d476963) on master excluding skipped/optionally flaky jobs)

Metric Before (avg of 4 commits) After (5b5026c) Δ
Time to green (first job → last success) 21m42s 11m22s −48%
Full wall clock 22m53s 11m22s −50%
Job slots 308 162 −47%
Runner-minutes 504 387 −23%
Blocking job failures 5 / 3 / 7 / 5 0 eliminated
E2E suite span 16m00s 8m39s −46%
E2E runner-minutes 370 259 −30%
E2E job slots 232 91 −61%
Playwright runner-minutes 56.5 50 −12%
Lint + unit + integration runner-minutes 65.5 64 −2%
Build runner-minutes 5.3 6 flat

Baselines are push runs; the after run is a pull_request run with a colder cache tag. E2E app coverage is identical (232 apps both sides).


CI-only changes on top of #25667 (merged). None drops a test or a variant.

Retry flaky tests, and keep reporting them. Every first-attempt CI run I sampled failed at least one job, but CI never retried a single test (retries: 0 for E2E outside dev mode and for browser integration). Now Playwright retries a failed test once in CI. A test that only passes on retry doesn't fail the job, but it still shows up:

  • the job summary lists it;
  • the job runs a Flaky tests passed on retry step;
  • each such test gets a Passed on retry: … warning annotation, and its failed attempt's traces are uploaded even though the job passed;
  • weekly-flaky-tests.mjs counts flaky tests per test from those annotations, also in jobs where another test failed for good, in a new "Passed on retry" column. It now also fetches annotations for passing jobs that ran the flaky step, so it makes more API calls than before.

test:detect-flaky passes --retries=0, so new tests still have to pass on every run. The apps that set retries: 0 themselves (the Cloudflare, Hono and send-to-Sentry apps) keep it.

Four short E2E apps per job. The median required E2E job runs about 55 s but waits about 77 s for a runner, and it repeats checkout, artifact download, Playwright install and prepare for every app. The apps on the 2 vCPU runner now run four per job, one after another, which cuts their jobs from 189 to 48 (231 → 90 E2E jobs in total). Details:

  • Each app has its own continue-on-error steps with the old time limits, its own directory and its own traces, so a failed app doesn't stop the others. Check E2E results then fails the job.
  • Apps only share a job with apps on the same Playwright version.
  • Between apps, the job stops only the previous app's leftovers: processes carrying its SENTRY_E2E_APP env marker (inherited by detached servers too) and the containers created since it started.
  • Apps with sentryTest.runner keep a job of their own.
  • Required checks are unaffected: the ruleset only requires All required jobs passed or were skipped.
  • getTestMatrix.mjs fails the matrix step if batching drops, duplicates or changes an entry (231 entries today), or if two entries share a label.
  • Estimated from today's 4 vCPU timings, the summed per-app time of a job is about 170 s at p50 and up to about 250 s. That's still shorter than the jobs of the slower apps on 4 vCPUs.

Warm the E2E pnpm store nightly. All E2E jobs share one Namespace cache volume per tag, and the last job to finish overwrites what the others saved. So the store never holds every app's packages, and the downloads it misses are the ones that crawl and time out the builds. The new warm-e2e-cache.yml runs on both E2E tags:

  • it installs every test app (with --ignore-scripts) against the tarballs of the latest develop build;
  • it doesn't run pnpm store prune: the apps install outside the cache volume, so their files are copies, and prune would empty the store. Instead it starts from an empty store when the volume is ≥70% full (Namespace stops cloning a cache that outgrows its limit);
  • it then reinstalls every app with --offline to prove the store alone is complete, and fails if not;
  • it runs with pnpm's verify-deps install off and fails if the workspace root got a node_modules;
  • jobs fork the newest generation, so they start from a complete store and only add to it.

This depends on Namespace spreading new generations across its nodes, so treat it as likely to help, not proven. The other half of the idea, a runner image with Playwright, Bun, Deno and SAM already installed, is a runner-profile setting in the Namespace dashboard, so it isn't in this PR.

prepare-e2e-test now combines two smaller actions, setup-e2e-tests (runs once per job) and prepare-e2e-app (runs once per app). optional-e2e.yml still uses it unchanged.

Faster Check lockfile. The job had no cache volume, so pnpm dedupe --check fetched the metadata of every dependency again on each run: about 300 MB from npm (1.2 GB unpacked), which took 6+ minutes and timed out #25667 three times. It also ran a full pnpm install only to check that pnpm-lock.yaml is stable. Now the job keeps pnpm's caches on its own volume (sentry-javascript-lockfile, -pr for PRs), checks the lockfile with pnpm install --lockfile-only, and runs on 2 vCPUs, because both checks only resolve. Locally, the two checks took 25 s cold and 4 s warm, and --lockfile-only changed pnpm-lock.yaml exactly like a full install for a changed range and for a new dependency. It also runs on CI changes now, so changes like this one get tested.

Bounded retries, and the Actions cache for the slowest downloads. Single downloads from npm sometimes crawl at kB/s for longer than a job's timeout, and pnpm, npm and Playwright never give up on them. These were the most common infra failures left:

  • install-pnpm replaces pnpm/action-setup, which installed a bootstrap pnpm with npm and then downloaded a second 24 MB pnpm in every job. It downloads the standalone @pnpm/exe binary directly, checks it against the registry's integrity, aborts transfers under 256 kB/s so a retry gets a new connection, and keeps the binary in the Actions cache (~24 MB, saved outside PRs only).
  • The root pnpm install and the Playwright browser download get bounded attempts (300 s, 300 s, then 600 s). apt is never killed midway.
  • Chromium jobs skip install-deps unless ldd finds a missing library. On the runner image, the only packages it installed for Chromium were 9 font packages, which took 6–10 min on a cold cache, and no test compares pixels. WebKit and Firefox still install their dependencies.
  • Each npm install attempt in a Node integration suite's setup hook gets a 50 s timeout (the hook limit goes from 120 s to 210 s), and the base branch's Node 24 jobs keep the suites' npm cache in the Actions cache, one entry per shard and ISO week. The weekly key means suite edits don't save extra copies, and versions the suites stopped using drop out. The repo's Actions cache is already at its 10 GB limit, mostly with stale dependencies-* caches of the old workflow, so these are the only two new entries.
  • node-express-static's variant and the two Angular canary variants had labels shared with other apps. They now have their own.

via @joshuarli.

--

View Junior Session [Sentry]

sentry-junior Bot and others added 3 commits October 22, 2026 17:59
189 of the 231 required E2E jobs finish in about 42 s (p50) on the 4 vCPU
runner, so they pay for cores they don't use. They now run on the small
profile. The 20 apps whose jobs take longer than about 90 s set
`sentryTest.runner` to keep the 4 vCPU profile.

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
…2E caches

Every sampled first-attempt CI run failed at least one job, and CI never
retried a single test. Playwright now retries a failed test once in CI. A
test that passes on retry no longer fails the job, but it is listed in the
job summary, and the weekly flaky test report now also counts it, so
flakes stay visible. The flaky test detector keeps `--retries=0`.

The required E2E apps on the 2 vCPU runner now run four per job, one
after another, which cuts their jobs from 189 to 48. Setup, Playwright and
the runner queue are paid once per four apps. Every app and variant still
runs, a failed app does not stop the others, and the job fails if any app
failed. Apps that set `sentryTest.runner` keep a job of their own.

The E2E jobs share one cache volume, where the last job to finish
overwrites what the others saved. A nightly job installs every test app
into the pnpm store on both E2E cache tags and prunes packages no app
uses, so later jobs fork a complete store that stays under the size limit.

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Optional E2E tests

Some optional checks were not successful. They do not block the merge. 1 failing, 63 successful. See the run.

Check Status
❌ CI: Optional E2E Tests / E2E node-anthropic-send-to-sentry (latest) Test (optional) Failing after 15s Details
Successful checks
Check Status
✅ CI: Optional E2E Tests / E2E angular-21 (canary) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E angular-22 (canary) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-14 (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-14 (canary) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E astro-7-static Test (optional) Successful in 50s Details
✅ CI: Optional E2E Tests / E2E cloudflare-pi-durable Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-pi-durable (latest) Test (optional) Successful in 23s Details
✅ CI: Optional E2E Tests / E2E deno (latest) Test (optional) Successful in 47s Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (deno) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E cloudflare-workers-send-to-sentry Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nextjs-15 (latest) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E astro-7 Test (optional) Successful in 58s Details
✅ CI: Optional E2E Tests / E2E cloudflare-flue-send-to-sentry Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-agent Test (optional) Successful in 18s Details
✅ CI: Optional E2E Tests / E2E cloudflare-mastra Test (optional) Successful in 27s Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (canary, webpack) Test (optional) Successful in 5m Details
✅ CI: Optional E2E Tests / E2E node-eve Test (optional) Successful in 34s Details
✅ CI: Optional E2E Tests / E2E gen-ai-libraries Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E debug-id-sourcemaps Test (optional) Successful in 26s Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (cloudflare, latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-15 (turbo) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (canary, turbopack) Test (optional) Successful in 4m Details
✅ CI: Optional E2E Tests / E2E gen-ai-libraries (cloudflare) Test (optional) Successful in 25s Details
✅ CI: Optional E2E Tests / E2E cloudflare-think (ai v6) Test (optional) Successful in 44s Details
✅ CI: Optional E2E Tests / E2E supabase-nextjs Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-openai-decisions-send-to-sentry Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-think Test (optional) Successful in 47s Details
✅ CI: Optional E2E Tests / E2E nextjs-16-standalone (canary, turbopack) Test (optional) Successful in 46s Details
✅ CI: Optional E2E Tests / E2E node-flue (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-openai-decisions-send-to-sentry (latest) Test (optional) Successful in 44s Details
✅ CI: Optional E2E Tests / E2E nextjs-16-tunnel (cloudflare) Test (optional) Successful in 35s Details
✅ CI: Optional E2E Tests / E2E nextjs-orpc Test (optional) Successful in 59s Details
✅ CI: Optional E2E Tests / E2E node-fastify-6 Test (optional) Successful in 22s Details
✅ CI: Optional E2E Tests / E2E node-openai-send-to-sentry (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-flue Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-eve (latest) Test (optional) Successful in 35s Details
✅ CI: Optional E2E Tests / E2E node-mastra Test (optional) Successful in 40s Details
✅ CI: Optional E2E Tests / E2E node-express-send-to-sentry Test (optional) Successful in 38s Details
✅ CI: Optional E2E Tests / E2E node-anthropic-send-to-sentry Test (optional) Successful in 46s Details
✅ CI: Optional E2E Tests / E2E node-eve (orchestrion) Test (optional) Successful in 44s Details
✅ CI: Optional E2E Tests / E2E vue-3 (no Options API) Test (optional) Successful in 50s Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps Test (optional) Successful in 20s Details
✅ CI: Optional E2E Tests / E2E nuxt-5 Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-openai-send-to-sentry Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-express-mcp-v1 Test (optional) Successful in 11s Details
✅ CI: Optional E2E Tests / E2E vue-3 (canary) Test (optional) Successful in 53s Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (vinext) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E react-router-7-framework-instrumentation Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (vinext, bun) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E tanstackstart-react-cloudflare Test (optional) Successful in 18s Details
✅ CI: Optional E2E Tests / E2E node-fastify-6 (tracer provider) Test (optional) Successful in 18s Details
✅ CI: Optional E2E Tests / E2E react-send-to-sentry Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nuxt-3 (canary) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E solidstart-2 Test (optional) Successful in 34s Details
✅ CI: Optional E2E Tests / E2E nuxt-4-static (canary) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps (static / nuxt generate) Test (optional) Successful in 22s Details
✅ CI: Optional E2E Tests / E2E node-mastra (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-pi-durable (latest) Test (optional) Successful in 20s Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps (client source maps kept) Test (optional) Successful in 22s Details
✅ CI: Optional E2E Tests / E2E nuxt-4 (canary) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E node-pi-durable Test (optional) Successful in 19s Details
✅ CI: Optional E2E Tests / E2E node-express-send-to-sentry-static Test (optional) Successful in 36s Details
✅ CI: Optional E2E Tests / E2E node-express-mcp-v2 Test (optional) Successful in 13s Details

sentry-junior Bot and others added 5 commits October 22, 2026 19:40
pnpm dedupe --check fetches package metadata from npm, and PR runs of
this job regularly take 8+ minutes, so the 10 minute limit cancels it.

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
- Warm cache: disable pnpm's verify-deps install, drop `pnpm store prune`
  (it empties a store whose packages were copied across filesystems),
  start from an empty store when the volume is mostly full, and prove the
  store is complete with an offline reinstall of every app.
- Upload Playwright traces for tests that passed on retry.
- Count flaky tests per test in the weekly report via a warning annotation
  per test, also in jobs that failed.
- Scope clean-up between batched apps to the previous app's processes
  (by an inherited env marker) and the containers created since it started.
- Fail the matrix step if batching drops, duplicates or changes an entry.

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
…entry-javascript into ci/flaky-retries-and-batched-e2e

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
`pnpm dedupe --check` reads the metadata of every dependency, about
300 MB from npm, and the job had no cache volume, so every run fetched
it again (6+ min). It also ran a full `pnpm install` just to check that
pnpm-lock.yaml is stable, which `--lockfile-only` does without
downloading packages. Both checks only resolve, so the job now runs on
the 2 vCPU runner with its own cache volume.

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
Base automatically changed from ci/e2e-small-runners to develop October 22, 2026 20:21
@getsentry getsentry deleted a comment from github-actions Bot Oct 22, 2026
sentry-junior Bot and others added 2 commits October 22, 2026 20:40
 into ci/flaky-retries-and-batched-e2e

# Conflicts:
#	.github/workflows/build.yml
#	dev-packages/e2e-tests/README.md

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
The weekly report matched a test's passed-on-retry annotation to its
failure annotations by test name only. Two apps of one batched E2E job
can run tests with the same name, so a flake in one app marked the same
test of the other app as passed on retry too. Match by path and name;
the path names the app's directory.

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Compared: 1737fc4 (PR test merge using develop at 3aafe9e)

Against: 3aafe9e (develop) · base run #63239

✅ Baseline matches: this diff contains only this PR's changes.

Path Size % Change Change
@sentry/browser 29.91 kB - -
@sentry/browser - with treeshaking flags 28.05 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.95 kB - -
@sentry/browser (incl. Tracing) 52.1 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 52.1 kB - -
@sentry/browser (incl. Tracing, Profiling) 55.05 kB - -
@sentry/browser (incl. Tracing, Replay) 91.85 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 80.72 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 96.58 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 109.52 kB - -
@sentry/browser (incl. Feedback) 47.42 kB - -
@sentry/browser (incl. sendFeedback) 34.97 kB - -
@sentry/browser (incl. FeedbackAsync) 40.07 kB - -
@sentry/browser (incl. Metrics) 30.93 kB - -
@sentry/browser (incl. Logs) 31.21 kB - -
@sentry/browser (incl. Metrics & Logs) 31.84 kB - -
@sentry/react 31.74 kB - -
@sentry/react (incl. Tracing) 54.42 kB - -
@sentry/vue 38.07 kB - -
@sentry/vue (incl. Tracing) 55.03 kB - -
@sentry/svelte 29.93 kB - -
@sentry/remix (Remix 3 client bundle) 57.11 kB - -
CDN Bundle 31.67 kB - -
CDN Bundle (incl. Tracing) 52.61 kB - -
CDN Bundle (incl. Logs, Metrics) 33.87 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 54.55 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 74.8 kB - -
CDN Bundle (incl. Tracing, Replay) 90.29 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 92.22 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 96.43 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 98.38 kB - -
CDN Bundle - uncompressed 93.29 kB - -
CDN Bundle (incl. Tracing) - uncompressed 156.14 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.84 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 162.11 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 229.91 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 276.34 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 282.3 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 290.05 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 295.99 kB - -
@sentry/nextjs (client) 57.3 kB - -
@sentry/sveltekit (client) 52.48 kB - -
@sentry/core/server 41.85 kB - -
@sentry/core/browser 13.81 kB - -
@sentry/node 152.96 kB - -
@sentry/node/import (ESM hook with diagnostics-channel injection) 81.58 kB +0.01% +1 B 🔺
@sentry/node - without tracing 94.57 kB - -
@sentry/node - without channel injection 133.6 kB +0.01% +1 B 🔺
@sentry/aws-serverless 102.74 kB - -
@sentry/cloudflare (withSentry) - minified 192.31 kB - -
@sentry/cloudflare (withSentry) 478.7 kB - -
@sentry/nextjs/cloudflare (withSentry) - minified 213.28 kB - -

View base workflow run

Single npm and Playwright downloads on Namespace runners sometimes crawl
at kB/s for longer than a job's timeout, and none of pnpm, npm or
Playwright give up on them.

- pnpm: download the standalone binary directly, verify it against the
  registry's integrity, abort and retry slow transfers, and keep it in
  the Actions cache. This replaces pnpm/action-setup's two downloads.
- Root pnpm install: bounded attempts, then a longer final one.
- Playwright: bounded browser download attempts. Chromium only installs
  system dependencies when a library is missing; on the runner image
  those were just fonts, and no test compares pixels.
- Node integration: each npm install attempt in a suite's setup hook
  gets its own timeout, and the base branch keeps the suites' npm cache
  in the Actions cache, per shard and week.
- E2E: give node-express-static's and the Angular canary variants their
  own labels, and fail the matrix on duplicate labels.

Co-Authored-By: Joshua Li <14209781+joshuarli@users.noreply.github.com>
@joshuarli
joshuarli marked this pull request as ready for review October 22, 2026 21:43
@joshuarli
joshuarli requested review from a team as code owners October 22, 2026 21:43
@joshuarli
joshuarli requested review from Lms24, andreiborza, isaacs and logaretm and removed request for a team October 22, 2026 21:43
@joshuarli joshuarli changed the title ci: Retry flaky Playwright tests, batch short E2E apps and warm the E2E caches ci: batch short e2e apps (+re-rightsize), prewarm e2e caches, retry flaky Playwright tests Oct 22, 2026
@joshuarli
joshuarli enabled auto-merge (squash) October 22, 2026 22:35

@JPeer264 JPeer264 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this should land as is. There are slow downloads, which should be fixed upstream. We should first only focus on caching properly.

Having 12minutes is already really bad. We aimed for having 8ish minutes per run.

Blocking this for now and I'm clarifying this internally.

@Lms24 Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not reviewable. It looks like this PR combines 4 changes and has a far too long and tedious description. Please split up the changes into individual PRs.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants