[eas-cli] add eas simulator:start command#3625
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
ed5e0bd to
b188388
Compare
|
Subscribed to pull request
Generated by CodeMention |
There was a problem hiding this comment.
Pull request overview
Adds an (hidden) experimental eas simulator:start command to create a remote device run session on EAS, poll the job run logs until connection env vars appear, and present connection details to the user.
Changes:
- Add GraphQL mutation/query wrappers for creating and fetching
DeviceRunSession+ regenerate GraphQL types/schema. - Introduce
eas simulator:startcommand with polling + log parsing to extract exported env vars. - Add
getJobRunUrlhelper for linking to job runs in the Expo website UI.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/eas-cli/src/graphql/queries/DeviceRunSessionQuery.ts | Adds query for fetching a device run session (including turtle job run status/log URLs). |
| packages/eas-cli/src/graphql/mutations/DeviceRunSessionMutation.ts | Adds mutation for creating a device run session. |
| packages/eas-cli/src/graphql/generated.ts | Regenerates GraphQL types to include device run session schema additions. |
| packages/eas-cli/src/commands/simulator/start.ts | New experimental command implementing session creation, polling, and log parsing to surface env vars. |
| packages/eas-cli/src/build/utils/url.ts | Adds getJobRunUrl for deep-linking to job run pages. |
| packages/eas-cli/graphql.schema.json | Updates schema introspection to include new server fields/types. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!baseUrl || !authToken) { | ||
| pollSpinner.fail(`Timed out waiting for ${flags.type} daemon to start`); | ||
| throw new Error( | ||
| `Timed out after ${Math.round( | ||
| POLL_TIMEOUT_MS / 1000 |
There was a problem hiding this comment.
On timeout/failure this command throws without attempting to stop the created device run session, which may leave a remote session running and consuming resources. Consider a best-effort cleanup (e.g. call the stopDeviceRunSession mutation in a finally/error path, or at least print the session id with guidance on how to stop it).
There was a problem hiding this comment.
I will do it as follow up
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3625 +/- ##
==========================================
- Coverage 55.70% 55.48% -0.22%
==========================================
Files 852 856 +4
Lines 36549 36795 +246
Branches 7642 7645 +3
==========================================
+ Hits 20356 20412 +56
- Misses 14774 16288 +1514
+ Partials 1419 95 -1324 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
b188388 to
e6ad3f5
Compare
| ).toString(); | ||
| } | ||
|
|
||
| export function getJobRunUrl( |
There was a problem hiding this comment.
| export function getJobRunUrl( | |
| export function getBareJobRunUrl( |
I remember whipping up that page as a temporary measure before adding we had workflow run page. Maybe this will make it clear it's not to be used in general (especially not if workflow run is available)? Or maybe we want to mark it as deprecated?
| function toTypeFlagValue(type: DeviceRunSessionType): string { | ||
| return type.toLowerCase().replaceAll('_', '-'); | ||
| } |
There was a problem hiding this comment.
Let's use a Record<DeviceRunSessionType, string> instead? Or Record<string, DeviceRunSessionType>, whatever makes sense.
| export default class SimulatorStart extends EasCommand { | ||
| static override hidden = true; | ||
| static override description = | ||
| '[EXPERIMENTAL] start a remote simulator session on an EAS VM and get the credentials to connect to it with the CLI tool of your choice'; |
There was a problem hiding this comment.
| '[EXPERIMENTAL] start a remote simulator session on an EAS VM and get the credentials to connect to it with the CLI tool of your choice'; | |
| '[EXPERIMENTAL] start a remote simulator session on EAS and get the credentials to connect to it with the CLI tool of your choice'; |
|
|
||
| static override flags = { | ||
| platform: Flags.option({ | ||
| description: 'Platform to start the simulator session for', |
There was a problem hiding this comment.
| description: 'Platform to start the simulator session for', | |
| description: 'Device platform', |
?
| platform: Flags.option({ | ||
| description: 'Platform to start the simulator session for', | ||
| options: ['android', 'ios'] as const, | ||
| default: 'ios', |
There was a problem hiding this comment.
| default: 'ios', |
hmmmm not sure
| jobRunStatus === JobRunStatus.Finished | ||
| ) { | ||
| throw new Error( | ||
| `Turtle job run for device run session ${deviceRunSessionId} is ${jobRunStatus.toLowerCase()} before the ${flags.type} daemon was ready.` |
There was a problem hiding this comment.
| `Turtle job run for device run session ${deviceRunSessionId} is ${jobRunStatus.toLowerCase()} before the ${flags.type} daemon was ready.` | |
| `Turtle job run for device run session ${deviceRunSessionId} ${jobRunStatus.toLowerCase()} before the ${flags.type} daemon was ready.` |
| const BASE_URL_ENV_VAR = 'AGENT_DEVICE_DAEMON_BASE_URL'; | ||
| const AUTH_TOKEN_ENV_VAR = 'AGENT_DEVICE_DAEMON_AUTH_TOKEN'; |
There was a problem hiding this comment.
It feels like in this file we mix being ready to support multiple backends (extractor for type) and hardcoding agent-device (these consts).
Maybe it would be simpler if the extractor was returning { ready, message } and if ready we stop polling and print message? Then all agent-device, agent-simulator-specific config would be inside that one function?
| ): Promise<{ baseUrl?: string; authToken?: string }> { | ||
| let text: string; | ||
| try { | ||
| const response = await fetch(url); |
There was a problem hiding this comment.
until we move to the nicer way of providing these which we discussed in slack I think fetching and parsing logs is going to be common for all backends so we might want to do it before extraction
| } | ||
|
|
||
| Log.newLine(); | ||
| Log.log(`🔑 Run the following in your shell to attach the ${flags.type} to the EAS VM:`); |
There was a problem hiding this comment.
| Log.log(`🔑 Run the following in your shell to attach the ${flags.type} to the EAS VM:`); | |
| Log.log(`🔑 Run the following in your shell to attach to ${flags.type}:`); |
| return line; | ||
| } | ||
|
|
||
| function extractExportedEnvValue(text: string, varName: string): string | undefined { |
There was a problem hiding this comment.
i wonder if we shouldn't parse this in the eas function…
6e20797 to
d07eb7f
Compare
9a5e1dc to
ebbe177
Compare
| await cloneAgentDeviceAsync({ packageVersion, env, logger }); | ||
|
|
||
| logger.info('Installing agent-device dependencies.'); | ||
| await spawn('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], { |
There was a problem hiding this comment.
Can we try using bun as package manager and runner? Might be faster?
| await fs.promises.mkdir(RUN_DIR, { recursive: true }); | ||
|
|
||
| logger.info('Ensuring cloudflared is installed.'); | ||
| await ensureCloudflaredInstalledAsync({ env, logger }); |
There was a problem hiding this comment.
I guess it would be nicer to have had these as separate EAS functions (for this to be a function group) but for v-1 it's ok i guess
There was a problem hiding this comment.
Will come back to it in follow up
| child.unref(); | ||
| logger.info(`Started detached process (pid ${child.pid}).`); | ||
| } finally { | ||
| fs.closeSync(fd); |
There was a problem hiding this comment.
I presume closing this does not prevent the command from writing to this file?
| }); | ||
|
|
||
| logger.info('Waiting for the daemon to report its HTTP port.'); | ||
| const daemonPort = await waitForMatchInLogAsync({ |
There was a problem hiding this comment.
i think port is gettable from the file too
ebbe177 to
e7fc28b
Compare
|
⏩ The changelog entry check has been skipped since the "no changelog" label is present. |

Why
Part of ENG-20714
Add hidden and experimental eas simulator:start command that starts remote agent device session on EAS server and outputs exportend env vars that allow to connect with it.
How
Use createDeviceSessionRun to start the session. Wait until our job run outputs the exports in the logs and forward them to the user. This is quite hacky but good for v-1 so we can start playing with it now internally.
Test Plan
Test manually