From 62769ccaf4a160659e80029d204f5e91b574c7ad Mon Sep 17 00:00:00 2001 From: Gabe Debes Date: Wed, 8 Jul 2026 22:29:06 -0700 Subject: [PATCH] [build-tools] pass metrics CORS origin to serve-sim --- CHANGELOG.md | 1 + .../__tests__/remoteDeviceRunSession.test.ts | 36 +++++++++++++++++++ .../src/steps/utils/remoteDeviceRunSession.ts | 25 +++++++++++++ 3 files changed, 62 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e365604f5..8e8cc69c5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This is the log of notable changes to EAS CLI and related packages. ### 🎉 New features - [eas-cli] Add `eas browse` to open the project page on expo.dev in a web browser, with an optional page argument (e.g. `eas browse build`, `eas browse hosting`) for project subpages and a `--no-browser` flag to print the URL. `eas open` is now an alias of `eas browse`. ([#4003](https://github.com/expo/eas-cli/pull/4003) by [@jonsamp](https://github.com/jonsamp)) +- [build-tools] Pass a metrics CORS origin to serve-sim so the dashboard can read a session's live CPU/memory stream cross-origin. ([#3990](https://github.com/expo/eas-cli/pull/3990) by [@gwdp](https://github.com/gwdp)) ### 🐛 Bug fixes diff --git a/packages/build-tools/src/steps/utils/__tests__/remoteDeviceRunSession.test.ts b/packages/build-tools/src/steps/utils/__tests__/remoteDeviceRunSession.test.ts index d1ef10adc7..ecbf59e0c5 100644 --- a/packages/build-tools/src/steps/utils/__tests__/remoteDeviceRunSession.test.ts +++ b/packages/build-tools/src/steps/utils/__tests__/remoteDeviceRunSession.test.ts @@ -7,6 +7,8 @@ import { Sentry } from '../../../sentry'; import { turtleFetch } from '../../../utils/turtleFetch'; import { fetchServeSimTurnArgsAsync, + getMetricsCorsOrigin, + metricsCorsOriginToServeSimArgs, turnIceServersToServeSimArgs, waitForDeviceRunSessionStoppedAsync, } from '../remoteDeviceRunSession'; @@ -80,6 +82,40 @@ function createEnvMock(): BuildStepEnv { return { DEVICE_RUN_SESSION_ID: 'drs-id' } as unknown as BuildStepEnv; } +describe(getMetricsCorsOrigin, () => { + it('returns the env origin, or undefined when unset/empty', () => { + expect( + getMetricsCorsOrigin({ + EAS_SIMULATOR_METRICS_CORS_ORIGIN: 'https://expo.dev', + } as BuildStepEnv) + ).toBe('https://expo.dev'); + expect(getMetricsCorsOrigin({} as BuildStepEnv)).toBeUndefined(); + expect( + getMetricsCorsOrigin({ EAS_SIMULATOR_METRICS_CORS_ORIGIN: '' } as BuildStepEnv) + ).toBeUndefined(); + }); +}); + +describe(metricsCorsOriginToServeSimArgs, () => { + it('returns no args when no origin is set', () => { + expect(metricsCorsOriginToServeSimArgs(undefined)).toEqual([]); + expect(metricsCorsOriginToServeSimArgs('')).toEqual([]); + }); + + it('builds one flag per comma-separated origin', () => { + expect(metricsCorsOriginToServeSimArgs('https://expo.dev')).toEqual([ + '--metrics-cors-origin', + 'https://expo.dev', + ]); + expect(metricsCorsOriginToServeSimArgs('https://expo.dev, https://staging.expo.dev')).toEqual([ + '--metrics-cors-origin', + 'https://expo.dev', + '--metrics-cors-origin', + 'https://staging.expo.dev', + ]); + }); +}); + describe(turnIceServersToServeSimArgs, () => { it('returns no args for an empty ICE server list', () => { expect(turnIceServersToServeSimArgs([])).toEqual([]); diff --git a/packages/build-tools/src/steps/utils/remoteDeviceRunSession.ts b/packages/build-tools/src/steps/utils/remoteDeviceRunSession.ts index f611b2c3f4..992eaa40b3 100644 --- a/packages/build-tools/src/steps/utils/remoteDeviceRunSession.ts +++ b/packages/build-tools/src/steps/utils/remoteDeviceRunSession.ts @@ -80,6 +80,29 @@ export function getNgrokAuthtokenOrThrow(env: BuildStepEnv): string { return authtoken; } +// Optional: the origin(s) allowed to read serve-sim's /metrics stream +// cross-origin (e.g. the expo.dev dashboard). Unset means no cross-origin +// access, which is fine for same-origin/loopback viewers. +export function getMetricsCorsOrigin(env: BuildStepEnv): string | undefined { + return env.EAS_SIMULATOR_METRICS_CORS_ORIGIN || undefined; +} + +// serve-sim's `--metrics-cors-origin` flag is repeatable, so a comma-separated +// env value (e.g. prod + staging dashboards) becomes one flag per origin. +export function metricsCorsOriginToServeSimArgs(origin: string | undefined): string[] { + if (!origin) { + return []; + } + const args: string[] = []; + for (const value of origin.split(',')) { + const trimmed = value.trim(); + if (trimmed) { + args.push('--metrics-cors-origin', trimmed); + } + } + return args; +} + const TurnIceServersSchema = z.array( z.object({ urls: z.array(z.string()), @@ -346,6 +369,7 @@ export async function startServeSimWithTunnelAsync( ): Promise<{ previewUrl: string; streamUrl: string }> { logger.info('Launching serve-sim with tunnel.'); const turnArgs = await fetchServeSimTurnArgsAsync(ctx, { env, logger }); + const metricsCorsArgs = metricsCorsOriginToServeSimArgs(getMetricsCorsOrigin(env)); const serveSim = spawnDetached({ command: 'npx', args: [ @@ -362,6 +386,7 @@ export async function startServeSimWithTunnelAsync( '--codec', 'webrtc', ...turnArgs, + ...metricsCorsArgs, ], env, });