Skip to content

test(e2e): Add Bun, Deno and Cloudflare variants to react-router-8-framework - #24598

Merged
JPeer264 merged 1 commit into
developfrom
jp/e2e-runtime-variants-poc
Oct 5, 2026
Merged

JPeer264 merged 1 commit into
developfrom
jp/e2e-runtime-variants-poc

Conversation

@JPeer264

@JPeer264 JPeer264 commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

POC for running one framework e2e app on several server runtimes instead of one app per runtime (Linear project P-JS-2537). react-router-8-framework now runs its full Playwright suite on Bun, Deno and Cloudflare (local workerd) as optional variants, next to the Node job.

Each runtime inits its own SDK, the way a user of that runtime would: Node @sentry/react-router, Bun @sentry/bun, Deno @sentry/deno (with --preload=@sentry/deno/import) and Cloudflare @sentry/cloudflare. @sentry/react-router then only provides the framework wrappers, so values from its init() (sdk.name, the runtime tag, the /__manifest filter) are Node-only, and the tests branch on that. Using the runtime SDKs is what makes the Bun variant work: @sentry/node creates no http.server span on Bun, @sentry/bun does.

A variant declares its runtime with a runtime field instead of the label. From it, both e2e jobs install Bun or Deno, the runner and CI set RUNTIME for the build and the assert command (read with the new getRuntime() from @sentry-internal/test-utils, and playwright.config.mjs picks the start command from it), and the runner copies the files named for that runtime over their base files in the temporary copy of the app. So the Cloudflare build gets vite.cloudflare.config.ts (@cloudflare/vite-plugin + sentryCloudflareVitePlugin) and entry.server.cloudflare.tsx, while Bun and Deno reuse the Node build. React Router has no option to pick a server entry, and this way a variant needs no framework or bundler config and no build.yml change. The Bun and Deno variants of hono-4 and hono-4-legacy set runtime too, because CI no longer reads the runtime from the label. The convention is in the e2e README.

Other expected differences: Express is not instrumented under bun run and there is no Express layer on workerd, so there the error transaction stays the request path and the meta tag names the http.server span. Deno is pinned to v2.9.0, because Deno 2.8 loses async context in socket callbacks and drops ioredis spans.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.52 kB - -
@sentry/browser - with treeshaking flags 27.68 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.57 kB - -
@sentry/browser (incl. Tracing) 51.45 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 51.46 kB - -
@sentry/browser (incl. Tracing, Profiling) 54.46 kB - -
@sentry/browser (incl. Tracing, Replay) 91.04 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 80.03 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 95.74 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 108.71 kB - -
@sentry/browser (incl. Feedback) 47.04 kB - -
@sentry/browser (incl. sendFeedback) 34.57 kB - -
@sentry/browser (incl. FeedbackAsync) 39.68 kB - -
@sentry/browser (incl. Metrics) 30.54 kB - -
@sentry/browser (incl. Logs) 30.82 kB - -
@sentry/browser (incl. Metrics & Logs) 31.48 kB - -
@sentry/react 31.36 kB - -
@sentry/react (incl. Tracing) 53.8 kB - -
@sentry/vue 37.51 kB - -
@sentry/vue (incl. Tracing) 54.33 kB - -
@sentry/svelte 29.55 kB - -
@sentry/remix (Remix 3 client bundle) 55.78 kB - -
CDN Bundle 31.22 kB - -
CDN Bundle (incl. Tracing) 51.98 kB - -
CDN Bundle (incl. Logs, Metrics) 33.46 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 53.92 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 74.21 kB - -
CDN Bundle (incl. Tracing, Replay) 89.56 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 91.53 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 95.72 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 97.71 kB - -
CDN Bundle - uncompressed 92.14 kB - -
CDN Bundle (incl. Tracing) - uncompressed 154.49 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 98.71 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 160.45 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 228.28 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 274.22 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 280.16 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 287.93 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 293.86 kB - -
@sentry/nextjs (client) 56.32 kB - -
@sentry/sveltekit (client) 51.87 kB - -
@sentry/core/server 40.59 kB - -
@sentry/core/browser 13.63 kB - -
@sentry/node 144.77 kB +0.01% +14 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 83.22 kB - -
@sentry/node - without tracing 93.32 kB +0.02% +10 B 🔺
@sentry/node - without channel injection 122.97 kB +0.01% +5 B 🔺
@sentry/aws-serverless 101.59 kB +0.01% +5 B 🔺
@sentry/cloudflare (withSentry) - minified 208.61 kB - -
@sentry/cloudflare (withSentry) 517.4 kB - -

View base workflow run

@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch from f08b065 to 9297a36 Compare September 23, 2026 05:27
@JPeer264 JPeer264 changed the title test(e2e): Add Bun, Deno and Cloudflare variants to react-router-8-framework test(e2e): Add Deno and Cloudflare variants to react-router-8-framework Sep 23, 2026
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch 2 times, most recently from d358a6c to 4dd044c Compare September 23, 2026 09:12
@JPeer264
JPeer264 removed this pull request from stack #24600 September 23, 2026 09:14
@JPeer264 JPeer264 changed the title test(e2e): Add Deno and Cloudflare variants to react-router-8-framework test(e2e): Add Bun, Deno and Cloudflare variants to react-router-8-framework Sep 23, 2026
@JPeer264
JPeer264 changed the base branch from develop to jp/node-suites-bun-build September 23, 2026 09:15
@JPeer264
JPeer264 added this pull request to stack #24613 September 23, 2026 09:15
@JPeer264
JPeer264 removed this pull request from stack #24613 September 23, 2026 09:51
@JPeer264
JPeer264 added this pull request to stack #24633 September 23, 2026 09:51
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch 2 times, most recently from adb5cb1 to fb48ca5 Compare September 23, 2026 10:02
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch 2 times, most recently from 51e4c78 to b94af9d Compare September 23, 2026 12:21
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch from b94af9d to 6e3b225 Compare September 23, 2026 12:24
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch from 6e3b225 to c2638e4 Compare September 23, 2026 13:23
Comment thread .github/workflows/build.yml
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch from c2638e4 to 90bb70c Compare September 23, 2026 13:31
@JPeer264
JPeer264 removed this pull request from stack #24633 September 23, 2026 16:19
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch from 5af7f99 to c5ae31e Compare September 27, 2026 14:03
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch 3 times, most recently from 2704c9c to 72f6d6c Compare September 28, 2026 08:07
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch 3 times, most recently from d0fbc38 to 8aefac0 Compare September 28, 2026 09:38
@JPeer264
JPeer264 force-pushed the jp/e2e-runtime-variants-poc branch 3 times, most recently from b789b78 to 57c28ca Compare September 28, 2026 14:29

@isaacs isaacs 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.

Making the name load-bearing feels like it might be a bit hazardous, but it's definitely not broken now. Could either fix with this PR, or make a follow-up issue.

Comment thread .github/workflows/build.yml Outdated
- name: Set up Bun
if:
matrix.test-application == 'node-exports-test-app' || contains(matrix.test-application, 'bun') ||
contains(matrix.label, 'bun')

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 would also match strings like turbopack bundler.

We could harden that a bit by pinning the test to the end of the string, matching our conventions:

Suggested change
contains(matrix.label, 'bun')
endsWith(matrix.label, '(bun)')

Perhaps worth doing the same for the other contains(matrix.label, 'bun') calls further up the file as well?

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.

Or, actually, if we do set matrix.runtime, then could just use that instead of keying on the name at all.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That got removed in the combination of this: #24598 (comment)

const RUNTIMES = ['bun', 'deno', 'cloudflare'];

/** Returns the runtime that a variant label like `my-app (cloudflare)` ends with, or `undefined`. */
export function getRuntimeFromLabel(label: string | undefined): string | undefined {

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 will turn on file replacement for tests that didn't opt in, which is not a problem today, but might be a surprise in the future.

getRuntimeFromLabel reads the runtime from the label text. Labels that already end in (bun), (deno) or (cloudflare) exist today: hono-4, hono-4-legacy, gen-ai-libraries (cloudflare). Those apps use the same entry.<runtime>.ts / instrument.<runtime>.ts file names, but their scripts pick the files directly. Nothing breaks today only because they have no src/entry.ts or src/instrument.ts (The base files are entry.node.ts / instrument.node.ts). If someone adds hono-4/src/instrument.ts later, the bun/deno variants will silently overwrite it with the runtime file in CI only, and a local in-folder run will act differently.

Also, the label is free text for display. Changing a label (for example if we changed something to react-router-8-framework (Cloudflare, local workerd)) would silently turn the replacement off, and the variant would build with the Node vite.config.ts.

Suggestion: make this an explicit variant field, for example { "runtime": "cloudflare", ... }. getTestMatrix.mjs already spreads variant fields into the matrix include, so CI can pass ${{ matrix.runtime }} to ci:copy-to-temp, and run.ts can read matchedVariant.runtime. The same field could also set RUNTIME for the assert step, which would remove the need for the test:assert:<runtime> scripts and the volta run workaround in the README.

Can definitely be a follow-up, though, if you'd rather put it off for now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah that is true. I like yours more actually to explicitly set the runtime, rather than taking it from the description. Could have been bad if there would be a node framework called bunny.js 😅 🐰

// the initial pageload to `/performance` gets 301-redirected to a trailing slash by react-router-serve
'url.path': { value: '/performance/', type: 'string' },
// the initial pageload to `/performance` gets 301-redirected to a trailing slash by react-router-serve, workerd does not
'url.path': { value: RUNTIME === 'cloudflare' ? '/performance' : '/performance/', type: 'string' },

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 ternary is repeated a few times. Could it maybe be put in a single place and re-used?

I'm thinking if there's another in the future RUNTIME that needs to behave like cloudflare, it'd be a change in one place.

Could actually put PERFORMANCE_PATH and SERVER_SDK_NAME (and maybe SERVER_PLATFORM) in tests/constants.ts next to RUNTIME. Then build the regex from the path. Each future runtime would then need one edit and not one edit per test.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah actually a trailing slash worked too on Cloudflare. I moved it back to a trailing slash URL and this ternary is gone now

…amework

Runs the same Playwright suite on Bun, Deno and Cloudflare (local workerd)
as optional variants of the existing app, instead of one app per runtime.
Each runtime inits its own SDK (`@sentry/bun`, `@sentry/deno`,
`@sentry/cloudflare`), and the tests read the runtime with the new
`getRuntime()` from `@sentry-internal/test-utils`.

A variant declares its runtime with a `runtime` field. From it, CI installs
Bun or Deno, the runner and CI set `RUNTIME` for the build and the assert
command, and the runner copies the files named for that runtime over their
base files in the temporary copy of the app, for example
`entry.server.cloudflare.tsx` over `entry.server.tsx` and
`vite.cloudflare.config.ts` over `vite.config.ts`. So a runtime variant
needs no framework or bundler config. The Bun and Deno variants of hono-4
and hono-4-legacy now set `runtime` too, because CI no longer reads the
runtime from the label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants