Skip to content

feat(platform-vega): support Vega SDK 0.24 and Fire TV devices - #196

Open
hristototov wants to merge 4 commits into
callstackincubator:mainfrom
hristototov:feat/vega-sdk-0-24
Open

hristototov wants to merge 4 commits into
callstackincubator:mainfrom
hristototov:feat/vega-sdk-0-24

Conversation

@hristototov

Copy link
Copy Markdown

Description

Modernizes @react-native-harness/platform-vega for Vega SDK 0.24 (React Native 0.83 for Vega). The old runner shelled out to the retired kepler binary, needed a running device with the app already installed, never forwarded the Metro port, and did not adapt Metro for Amazon's React Native fork, so it could not launch anything on current SDKs.

The runner now drives the vega CLI: it starts the Vega Virtual Device when needed and waits until it accepts package manager commands, checks a Fire TV by its serial, installs the .vpkg from HARNESS_APP_PATH with --buildType Debug, keeps reverse port forwarding alive so the app reaches Metro on localhost:8081 (falling back to vda reverse for other ports), launches and tracks the interactive component, streams device logs into the app session, and shuts down a virtual device it started.

import { vegaPlatform, vegaVirtualDevice, vegaPhysicalDevice } from '@react-native-harness/platform-vega';

export default {
  entryPoint: './index.js',
  // app.json "name" must match the interactive component id from manifest.toml
  appRegistryComponentName: 'com.example.myapp.main',
  runners: [
    vegaPlatform({ name: 'vega', device: vegaVirtualDevice(), bundleId: 'com.example.myapp' }),
    vegaPlatform({ name: 'fire-tv', device: vegaPhysicalDevice('G0X1AB1234567890'), bundleId: 'com.example.myapp' }),
  ],
};

Also included: a new apps/playground-vega app carrying the platform-agnostic playground suites, a Vega page on the website, a dispatch-only Vega job in e2e-tests.yml for a self-hosted runner, and removal of the dead Vega runner from apps/playground.

Related Issue

Closes #195

Context

Three changes outside the runner were necessary, and each was found by getting the app to run on the real device:

  • Metro config enhancer. Harness loads Metro's config directly and never runs the RN CLI plugin, so the enhancer supplies what react-native start gets from @amazon-devices/kepler-cli-platform: the kepler platform, the react-native to @amazon-devices/react-native-kepler redirect, and Kepler's InitializeCore as a run-before-main module. The default config points at upstream React Native's InitializeCore, which the redirect keeps out of the graph, so Metro silently dropped it and the app never initialized.
  • bundlePlatform on HarnessPlatform. Vega apps request platform=kepler from Metro while the harness platform id, test file suffix, and lock keys stay vega. Prewarming and startup detection now use bundlePlatform, defaulting to platformId, so nothing changes for other platforms.
  • Runtime drops react-native-url-polyfill. Importing it reads the legacy NativeModules bridge, which bridgeless runtimes do not have, so Harness initialization threw on every launch. The dev server URL is now parsed by hand.

Worth knowing before reviewing:

  • vega device install-app defaults to Release. A Debug package installed that way never gets the debug sandbox overrides and Vega OS kills it as AppNotResponding about 15 s after launch, so the runner always passes --buildType.
  • The device transport drops out for a moment after boot, after already answering is-connected, so readiness requires three consecutive successful package manager probes and installs retry.
  • Apps must set kepler.useSystemJsBundles: false: Harness serves the whole bundle from its own Metro rather than Amazon's split system bundles.
  • vda identifies devices by adb-style serials, so the non-8081 Metro port path works with a single attached device or a physical device whose serial vda reports. Documented on the Vega page.
  • @react-native-harness/ui has no Vega native module yet, so the ui/ suites are not part of the Vega app.
  • pnpm dedupe --check reports a second react-native@0.82.1 resolution introduced by the new app, alongside pre-existing unrelated candidates; left for a separate change.

Testing

Unit tests: 47 in platform-vega (runner flow, CLI wrapper parsing and retries, Metro enhancer, app session), new cases for bundlePlatform in config and the URL parsing in runtime. pnpm nx affected -t build,lint,test,typecheck is green for all 23 affected projects and nx release plan:check passes.

On the Vega Virtual Device (SDK 0.24.12112, CLI 1.4.2, Apple Silicon):

  • Full suite from a stopped device: Harness boots it, installs the package, all 13 suites pass (61 tests, 9 skipped, 1 todo), and shuts the device down afterwards.
  • Killing the app from outside mid-test fails the file within a second with a bridge-disconnect message.
  • Metro forced onto port 8082 works through vda reverse, and the mapping is removed on dispose.

The physical Fire TV Select 4K run is still outstanding: it needs the developer-mode code from the device and an Amazon account login for vega devmode login.

vega-virtual-device-harness-ui image

@vercel

vercel Bot commented Oct 9, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Callstack Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread apps/playground-vega/README.md Outdated

## How to run React Native Harness?

Build a debug package first. Debug builds load the JavaScript bundle from Metro, which is what Harness needs. The app opts out of Vega system bundles (`kepler.useSystemJsBundles` in `package.json`) because Harness serves the whole bundle itself:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Amazon enabled system bundles by default since SDK v0.20 and it should not affect the code that is a part of the app project

Having RNHarness setup for Vega relying on that opt-out flag will limit the adoption in the projects that are using system bundles

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed the opt-out. The enhancer now applies Amazon's own applySystemBundleConfig, so the default split-bundle setup works

bridgeTimeout: 120000,
testTimeout: 10000,
resetEnvironmentBetweenTestFiles: 'runtime',
unstable__enableMetroCache: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this flag is deprecated, and the default value for metro cache according to docs is already true

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed it (and the unused disableViewFlattening) from the app config

Comment thread website/src/docs/platforms/vega.mdx Outdated

## Building and deploying the app

Harness serves the whole JavaScript bundle from its own Metro server, so the app must be built as a regular bundle rather than a split "system bundle" one. Opt out of system bundles in the `kepler` section of `package.json`:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If I get it right, the only thing Harness does with Metro is providing its own custom resolver. This should not be a reason to opt-out of the whole split bundles setup that Amazon implemented for Vega

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped the opt-out instructions and documented that Harness honours system bundles, falling back to a regular bundle only when an app sets useSystemJsBundles: false

Comment thread website/src/docs/platforms/vega.mdx Outdated

- `@react-native-harness/ui` (`screen`, `userEvent`, screenshots) has no Vega native module yet, so test files importing it fail to load on Vega.
- Crash detection relies on the app process disappearing and on the device log stream (Vega OS reports the kill reason there, for example `AppNotResponding`). Minidumps are not collected.
- `Platform.OS` is `'kepler'` inside tests. The platform-specific file suffix for Harness stays `vega`, for example `settings.vega.harness.ts`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That's not a limitation of Harness, that's just specific thing to Vega (Kepler is/was a working name and they never replaced that)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed it from Limitations and kept the useful part

Comment thread website/src/docs/platforms/vega.mdx Outdated
- `@react-native-harness/ui` (`screen`, `userEvent`, screenshots) has no Vega native module yet, so test files importing it fail to load on Vega.
- Crash detection relies on the app process disappearing and on the device log stream (Vega OS reports the kill reason there, for example `AppNotResponding`). Minidumps are not collected.
- `Platform.OS` is `'kepler'` inside tests. The platform-specific file suffix for Harness stays `vega`, for example `settings.vega.harness.ts`.
- React Native for Vega 0.83 runs the New Architecture only.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

again, that's not a limitation of Harness, that's how Vega apps were implemented from the beginning, there's no need to mention that here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed the bullet

Comment thread website/src/docs/guides/ci-cd.md Outdated

## Vega in CI

The Vega SDK and the Vega Virtual Device are not available on GitHub-hosted runners, so Vega jobs need a self-hosted macOS or Ubuntu runner with the SDK installed (`source ~/vega/env` in the runner's shell) and either a virtual device or a Fire TV in developer mode on the runner's network. Build the Debug package in an earlier step and pass it through the `app` input (see the [Vega platform guide](/docs/platforms/vega)). The repository's own `e2e-tests.yml` has an `e2e-vega` job of this shape, gated on `workflow_dispatch` with `platform: vega` and a runner carrying the `vega` label.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Would be great to add a permalink to e2e-tests.yml for any person reading the docs to go straight to example yaml

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Linked e2e-tests.yml to its GitHub page on main

...metroConfig.resolver,
platforms: [
...new Set([
...(metroConfig.resolver?.platforms ?? ['ios', 'android']),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

the default values should have 'kepler' preceding ios and android, otherwise it will try to require .android.[jt]s files first before looking for .kepler.[jt]s

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Put kepler first in resolver.platforms. The unit test asserts the order.

@mateusz1913 mateusz1913 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks good now, I'll try to run it on my end on a device with production test suite and will let you know how it goes

// Kepler first so its platform files win over the defaults.
platforms: [
...new Set([
KEPLER_PLATFORM,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

it should be in defaults, otherwise overriding is broken

...new Set([
  ...(metroConfig.resolver?.platforms ?? [KEPLER_PLATFORM, 'ios', 'android']),
  'native',
])

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

updated

@mateusz1913 mateusz1913 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Works on my end with physical device, great job @hristototov!

@V3RON V3RON left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the current head across all five axes. Ran locally on the PR branch: unit tests pass for platform-vega (51), runtime (128), config (27), bundler-metro (88) and jest (184); tsc -b is clean for all touched packages plus apps/playground-vega; eslint reports no new errors. No leftover react-native-url-polyfill references in code. The bundlePlatform plumbing, the serializer id pre-assignment (which mirrors Metro's baseJSBundle first loop exactly, with a proper regression test), the getWSServer rewrite (userinfo/IPv6/case all covered by tests), and the string module-id handling all check out. Docs are solid.

Everything else in my notes is Nit/Consider level — I'm only requesting changes for one thing:

The app session treats a CLI failure as an app exit. isAppState() returns false both for a confirmed "not running" answer and for any CLI failure (ok === false covers non-zero exit, the 30 s command timeout, and spawn errors). After the 10 s grace, a single transient probe failure — e.g. the device transport flap this PR itself documents around boot, or one runCommand timeout — is recorded as process-gone and fails the running test file with a false crash. The Android session guards exactly this two ways (catch around the poll treats an error as retryable, and process-gone is only declared after the process was observed running); this session has neither. Details and suggested fix on the inline comment.

Small fix, and the rest is good to go.


const pollTask = (async () => {
while (!stopPolling) {
const running = await isAppRunning();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

isAppRunning() here cannot distinguish "the app is not running" from "the probe failed": isAppState() in vega.ts resolves to false whenever runVegaQuietly returns ok: false, which covers a non-zero CLI exit, the 30 s runCommand timeout, and spawn errors — not just a confirmed false answer. So after startGraceMs, one transient failure of vega device is-app-running — e.g. the transport flap this PR documents around device boot, or a single slow CLI call — sets process-gone and fails the running test file with what looks like an app crash.

The Android session is deliberately built the other way around this (platform-android/src/app-session.ts): the poll catches probe errors and just retries, and process-gone is only declared after the process was observed running at least once.

Suggested fix (either works, first is probably cleanest):

  • Make the state probe three-valued — e.g. return 'unknown' when ok is false without a parseable affirmative/negative answer, and have the poll loop treat 'unknown' like running (skip and retry) so only a confirmed false can end the session. Optionally require 2–3 consecutive confirmed negatives before declaring the exit, which also covers a CLI that answers false while the transport is half-broken.
  • Or keep the boolean and wrap the poll in try/catch like Android does — but that alone isn't enough, because today a failed probe doesn't throw, it returns false.

This is the session's own failure-direction concern: a false app_exited mid-run is indistinguishable from a real crash in the report, and the fix is small.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

updated

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.

Support for modern Vega SDKs

3 participants