From db2f861e1dfd8e4fc9abaae979816513e767f982 Mon Sep 17 00:00:00 2001 From: szdziedzic Date: Tue, 11 Aug 2026 10:30:07 +0200 Subject: [PATCH 1/2] [eas-cli] Add --device flag to eas simulator Co-Authored-By: Claude Fable 5 --- .../simulator/__tests__/index.test.ts | 42 +++++++++++++++++++ .../eas-cli/src/commands/simulator/index.ts | 6 +++ packages/eas-cli/src/graphql/generated.ts | 7 ++++ 3 files changed, 55 insertions(+) diff --git a/packages/eas-cli/src/commands/simulator/__tests__/index.test.ts b/packages/eas-cli/src/commands/simulator/__tests__/index.test.ts index 3edf61a967..92f8c51c3b 100644 --- a/packages/eas-cli/src/commands/simulator/__tests__/index.test.ts +++ b/packages/eas-cli/src/commands/simulator/__tests__/index.test.ts @@ -411,6 +411,48 @@ describe(Simulator, () => { ); }); + it('forwards --device to the create mutation as deviceIdentifier', async () => { + const { command } = createCommand([ + '--platform', + 'ios', + '--non-interactive', + '--device', + 'iPhone 16 Pro', + ]); + await command.runAsync(); + + expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith( + graphqlClient, + expect.objectContaining({ deviceIdentifier: 'iPhone 16 Pro' }) + ); + }); + + it('trims --device before sending it', async () => { + const { command } = createCommand([ + '--platform', + 'ios', + '--non-interactive', + '--device', + ' iPhone 16 Pro ', + ]); + await command.runAsync(); + + expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith( + graphqlClient, + expect.objectContaining({ deviceIdentifier: 'iPhone 16 Pro' }) + ); + }); + + it('omits a blank --device', async () => { + const { command } = createCommand(['--platform', 'ios', '--non-interactive', '--device', ' ']); + await command.runAsync(); + + expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith( + graphqlClient, + expect.objectContaining({ deviceIdentifier: undefined }) + ); + }); + it('stops the simulator session when interrupted before the session is ready', async () => { const processExitSpy = jest.spyOn(process, 'exit').mockImplementation(code => { throw new Error(`process.exit(${code})`); diff --git a/packages/eas-cli/src/commands/simulator/index.ts b/packages/eas-cli/src/commands/simulator/index.ts index 07842900d8..8717cf59f6 100644 --- a/packages/eas-cli/src/commands/simulator/index.ts +++ b/packages/eas-cli/src/commands/simulator/index.ts @@ -68,6 +68,10 @@ export default class Simulator extends EasCommand { description: 'Human-readable name for the simulator session, shown in eas simulator:list and on expo.dev. Defaults to unnamed.', }), + device: Flags.string({ + description: + 'Virtual device to start for the session. On iOS, a Simulator device name or UDID (e.g. "iPhone 16 Pro"). On Android, an AVD hardware profile id (e.g. "pixel_7"). Defaults to a device chosen by the runner.', + }), type: Flags.option({ description: 'Type of simulator session to create', options: Object.values(DEVICE_RUN_SESSION_TYPE_FLAG_VALUES), @@ -134,6 +138,7 @@ export default class Simulator extends EasCommand { // The server rejects blank names, so trim here and treat a whitespace-only // --name as if it had been omitted rather than surfacing a validation error. const name = flags.name?.trim() || undefined; + const deviceIdentifier = flags.device?.trim() || undefined; await loadSimulatorEnvAsync(projectDir); const existingDeviceRunSessionId = process.env[EAS_SIMULATOR_SESSION_ID]; @@ -165,6 +170,7 @@ export default class Simulator extends EasCommand { platform, type: DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE[flags.type], packageVersion: flags['package-version'], + deviceIdentifier, maxRunTimeMinutes: flags['max-duration-minutes'], }); deviceRunSessionId = session.id; diff --git a/packages/eas-cli/src/graphql/generated.ts b/packages/eas-cli/src/graphql/generated.ts index 91fed12a8e..18e9b834b6 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -5095,6 +5095,13 @@ export type CreateDeviceRunSessionEventLogUploadSessionResult = { export type CreateDeviceRunSessionInput = { appId: Scalars['ID']['input']; + /** + * Identifier of the virtual device to start for the session. On iOS this is a + * Simulator device name or UDID (e.g. "iPhone 16 Pro"). On Android this is an + * AVD hardware profile id (e.g. "pixel_7"). If omitted, the runner picks a + * default device. + */ + deviceIdentifier?: InputMaybe; /** * Override for the underlying turtle job run's max run time, in minutes. Must * be non-negative and smaller than 120 (2 hours). Only customizable on paid From 1b4c0922ebe70644f5dad455d58d996c6871d838 Mon Sep 17 00:00:00 2001 From: szdziedzic Date: Tue, 11 Aug 2026 10:30:56 +0200 Subject: [PATCH 2/2] Update CHANGELOG.md Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 563d33bd8f..84d7bbdd72 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 Supabase integration foundation: GraphQL client, shared integration helpers, and provisioning utilities. ([#4130](https://github.com/expo/eas-cli/pull/4130) by [@gwdp](https://github.com/gwdp)) +- [eas-cli] Add `--device` flag to `eas simulator` for selecting the virtual device to start. ([#4172](https://github.com/expo/eas-cli/pull/4172) by [@szdziedzic](https://github.com/szdziedzic)) ### 🐛 Bug fixes