mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 17:50:32 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee650e744f | ||
|
|
e464d2c3dd |
No files matched your search
@@ -8,6 +8,7 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- [eas-cli] Add `--system-image-package` to `eas simulator` for selecting a validated x86_64 Android emulator system image. ([#4317](https://github.com/expo/eas-cli/pull/4317) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- [eas-cli] Update workflow run logs in real time, instead of every 10 seconds. ([#4228](https://github.com/expo/eas-cli/pull/4228) by [@AHGIJMKLKKZNPJKQR](https://github.com/AHGIJMKLKKZNPJKQR))
|
||||
- [build-tools] Add `expo-device-hub` web previews to Android web-preview-only, Agent Device, Argent, and Appium remote sessions. ([#4304](https://github.com/expo/eas-cli/pull/4304) by [@krystofwoldrich](https://github.com/krystofwoldrich))
|
||||
- [eas-cli] Add `eas workflow:ssh <workflow-job-id> [command...]` to open an ssh session on the worker running a workflow job. ([#4032](https://github.com/expo/eas-cli/pull/4032) by [@gwdp](https://github.com/gwdp))
|
||||
|
||||
+30
-15
@@ -2556,10 +2556,11 @@ _See code: [packages/eas-cli/src/commands/project/new.ts](https://github.com/exp
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas sim [-p android|ios] [--name <value>] [--device <value>] [--build-id <value> |
|
||||
--application-archive-url <value> | --expo-go] [--sdk-version <value>] [--launch-arg <value>...] [--open-url
|
||||
<value>] [--type agent-device|appium|argent|web-preview-only] [--package-version <value>] [--max-duration-minutes
|
||||
<value>] [--max-idle-time-minutes <value>] [--force] [--out-config-type env|dotenv] [--json] [--non-interactive]
|
||||
$ eas sim [-p android|ios] [--name <value>] [--device <value>] [--system-image-package <value>]
|
||||
[--build-id <value> | --application-archive-url <value> | --expo-go] [--sdk-version <value>] [--launch-arg
|
||||
<value>...] [--open-url <value>] [--type agent-device|appium|argent|web-preview-only]
|
||||
[--package-version <value>] [--max-duration-minutes <value>] [--max-idle-time-minutes <value>] [--force]
|
||||
[--out-config-type env|dotenv] [--json] [--non-interactive]
|
||||
|
||||
FLAGS
|
||||
-p, --platform=<option> Device platform
|
||||
@@ -2569,7 +2570,7 @@ FLAGS
|
||||
--build-id=<value> EAS Build to install and launch before the simulator session is ready.
|
||||
--device=<value> 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.
|
||||
"pixel_9"). Defaults to a device chosen by the runner.
|
||||
--expo-go Install and launch Expo Go matching the current project's Expo SDK before the
|
||||
simulator session is ready.
|
||||
--[no-]force [default: true] Create a new simulator session even when an existing simulator
|
||||
@@ -2596,6 +2597,10 @@ FLAGS
|
||||
Defaults to "latest" when omitted.
|
||||
--sdk-version=<value> Expo SDK version used to select Expo Go when --expo-go is passed. Defaults to
|
||||
the current project SDK.
|
||||
--system-image-package=<value> Android SDK system image package to use for the emulator (e.g.
|
||||
"system-images;android-36;google_apis_playstore;x86_64"). Only supported for
|
||||
Android and must use the x86_64 architecture required by EAS
|
||||
nested-virtualization runners.
|
||||
--type=<option> [default: agent-device] Type of simulator session to create. All session types
|
||||
include a web preview. agent-device, appium, and argent also include an
|
||||
automation interface; web-preview-only includes no automation interface.
|
||||
@@ -2723,10 +2728,11 @@ ALIASES
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas sim:start [-p android|ios] [--name <value>] [--device <value>] [--build-id <value> |
|
||||
--application-archive-url <value> | --expo-go] [--sdk-version <value>] [--launch-arg <value>...] [--open-url
|
||||
<value>] [--type agent-device|appium|argent|web-preview-only] [--package-version <value>] [--max-duration-minutes
|
||||
<value>] [--max-idle-time-minutes <value>] [--force] [--out-config-type env|dotenv] [--json] [--non-interactive]
|
||||
$ eas sim:start [-p android|ios] [--name <value>] [--device <value>] [--system-image-package <value>]
|
||||
[--build-id <value> | --application-archive-url <value> | --expo-go] [--sdk-version <value>] [--launch-arg
|
||||
<value>...] [--open-url <value>] [--type agent-device|appium|argent|web-preview-only]
|
||||
[--package-version <value>] [--max-duration-minutes <value>] [--max-idle-time-minutes <value>] [--force]
|
||||
[--out-config-type env|dotenv] [--json] [--non-interactive]
|
||||
|
||||
FLAGS
|
||||
-p, --platform=<option> Device platform
|
||||
@@ -2736,7 +2742,7 @@ FLAGS
|
||||
--build-id=<value> EAS Build to install and launch before the simulator session is ready.
|
||||
--device=<value> 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.
|
||||
"pixel_9"). Defaults to a device chosen by the runner.
|
||||
--expo-go Install and launch Expo Go matching the current project's Expo SDK before the
|
||||
simulator session is ready.
|
||||
--[no-]force [default: true] Create a new simulator session even when an existing simulator
|
||||
@@ -2763,6 +2769,10 @@ FLAGS
|
||||
Defaults to "latest" when omitted.
|
||||
--sdk-version=<value> Expo SDK version used to select Expo Go when --expo-go is passed. Defaults to
|
||||
the current project SDK.
|
||||
--system-image-package=<value> Android SDK system image package to use for the emulator (e.g.
|
||||
"system-images;android-36;google_apis_playstore;x86_64"). Only supported for
|
||||
Android and must use the x86_64 architecture required by EAS
|
||||
nested-virtualization runners.
|
||||
--type=<option> [default: agent-device] Type of simulator session to create. All session types
|
||||
include a web preview. agent-device, appium, and argent also include an
|
||||
automation interface; web-preview-only includes no automation interface.
|
||||
@@ -2803,10 +2813,11 @@ ALIASES
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas simulator:start [-p android|ios] [--name <value>] [--device <value>] [--build-id <value> |
|
||||
--application-archive-url <value> | --expo-go] [--sdk-version <value>] [--launch-arg <value>...] [--open-url
|
||||
<value>] [--type agent-device|appium|argent|web-preview-only] [--package-version <value>] [--max-duration-minutes
|
||||
<value>] [--max-idle-time-minutes <value>] [--force] [--out-config-type env|dotenv] [--json] [--non-interactive]
|
||||
$ eas simulator:start [-p android|ios] [--name <value>] [--device <value>] [--system-image-package <value>]
|
||||
[--build-id <value> | --application-archive-url <value> | --expo-go] [--sdk-version <value>] [--launch-arg
|
||||
<value>...] [--open-url <value>] [--type agent-device|appium|argent|web-preview-only]
|
||||
[--package-version <value>] [--max-duration-minutes <value>] [--max-idle-time-minutes <value>] [--force]
|
||||
[--out-config-type env|dotenv] [--json] [--non-interactive]
|
||||
|
||||
FLAGS
|
||||
-p, --platform=<option> Device platform
|
||||
@@ -2816,7 +2827,7 @@ FLAGS
|
||||
--build-id=<value> EAS Build to install and launch before the simulator session is ready.
|
||||
--device=<value> 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.
|
||||
"pixel_9"). Defaults to a device chosen by the runner.
|
||||
--expo-go Install and launch Expo Go matching the current project's Expo SDK before the
|
||||
simulator session is ready.
|
||||
--[no-]force [default: true] Create a new simulator session even when an existing simulator
|
||||
@@ -2843,6 +2854,10 @@ FLAGS
|
||||
Defaults to "latest" when omitted.
|
||||
--sdk-version=<value> Expo SDK version used to select Expo Go when --expo-go is passed. Defaults to
|
||||
the current project SDK.
|
||||
--system-image-package=<value> Android SDK system image package to use for the emulator (e.g.
|
||||
"system-images;android-36;google_apis_playstore;x86_64"). Only supported for
|
||||
Android and must use the x86_64 architecture required by EAS
|
||||
nested-virtualization runners.
|
||||
--type=<option> [default: agent-device] Type of simulator session to create. All session types
|
||||
include a web preview. agent-device, appium, and argent also include an
|
||||
automation interface; web-preview-only includes no automation interface.
|
||||
|
||||
@@ -508,7 +508,7 @@ describe(Simulator, () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('forwards --device to the create mutation as deviceIdentifier', async () => {
|
||||
it('forwards an iOS --device through the iOS-specific input', async () => {
|
||||
const { command } = createCommand([
|
||||
'--platform',
|
||||
'ios',
|
||||
@@ -520,7 +520,7 @@ describe(Simulator, () => {
|
||||
|
||||
expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith(
|
||||
graphqlClient,
|
||||
expect.objectContaining({ deviceIdentifier: 'iPhone 16 Pro' })
|
||||
expect.objectContaining({ ios: { deviceIdentifier: 'iPhone 16 Pro' } })
|
||||
);
|
||||
});
|
||||
|
||||
@@ -536,7 +536,7 @@ describe(Simulator, () => {
|
||||
|
||||
expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith(
|
||||
graphqlClient,
|
||||
expect.objectContaining({ deviceIdentifier: 'iPhone 16 Pro' })
|
||||
expect.objectContaining({ ios: { deviceIdentifier: 'iPhone 16 Pro' } })
|
||||
);
|
||||
});
|
||||
|
||||
@@ -544,10 +544,118 @@ describe(Simulator, () => {
|
||||
const { command } = createCommand(['--platform', 'ios', '--non-interactive', '--device', ' ']);
|
||||
await command.runAsync();
|
||||
|
||||
expect(mockCreateDeviceRunSessionAsync.mock.calls[0][1]).not.toHaveProperty('ios');
|
||||
expect(mockCreateDeviceRunSessionAsync.mock.calls[0][1]).not.toHaveProperty('android');
|
||||
});
|
||||
|
||||
it('forwards Android device and system image options through the Android-specific input', async () => {
|
||||
const { command } = createCommand([
|
||||
'--platform',
|
||||
'android',
|
||||
'--non-interactive',
|
||||
'--device',
|
||||
' pixel_8 ',
|
||||
'--system-image-package',
|
||||
' system-images;android-36;google_apis_playstore;x86_64 ',
|
||||
]);
|
||||
await command.runAsync();
|
||||
|
||||
expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith(
|
||||
graphqlClient,
|
||||
expect.objectContaining({ deviceIdentifier: undefined })
|
||||
expect.objectContaining({
|
||||
android: {
|
||||
deviceIdentifier: 'pixel_8',
|
||||
systemImagePackage: 'system-images;android-36;google_apis_playstore;x86_64',
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(mockCreateDeviceRunSessionAsync.mock.calls[0][1]).not.toHaveProperty('deviceIdentifier');
|
||||
});
|
||||
|
||||
it('forwards --system-image-package without requiring --device', async () => {
|
||||
const { command } = createCommand([
|
||||
'--platform',
|
||||
'android',
|
||||
'--non-interactive',
|
||||
'--system-image-package',
|
||||
'system-images;android-36-ext1;google_apis_ps16k;x86_64',
|
||||
]);
|
||||
await command.runAsync();
|
||||
|
||||
expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith(
|
||||
graphqlClient,
|
||||
expect.objectContaining({
|
||||
android: {
|
||||
systemImagePackage: 'system-images;android-36-ext1;google_apis_ps16k;x86_64',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects --system-image-package for iOS', async () => {
|
||||
const { command } = createCommand([
|
||||
'--platform',
|
||||
'ios',
|
||||
'--non-interactive',
|
||||
'--system-image-package',
|
||||
'system-images;android-36;google_apis_playstore;x86_64',
|
||||
]);
|
||||
|
||||
await expect(command.runAsync()).rejects.toThrow(
|
||||
'The --system-image-package flag is only supported for Android simulator sessions.'
|
||||
);
|
||||
expect(mockCreateDeviceRunSessionAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects a blank --system-image-package', async () => {
|
||||
const { command } = createCommand([
|
||||
'--platform',
|
||||
'android',
|
||||
'--non-interactive',
|
||||
'--system-image-package',
|
||||
' ',
|
||||
]);
|
||||
|
||||
await expect(command.runAsync()).rejects.toThrow(
|
||||
'The --system-image-package value cannot be blank.'
|
||||
);
|
||||
expect(mockCreateDeviceRunSessionAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
'system-images;android-36;google_apis_playstore',
|
||||
'platforms;android-36;google_apis_playstore;x86_64',
|
||||
'system-images;36;google_apis_playstore;x86_64',
|
||||
'system-images;android-36;google apis;x86_64',
|
||||
'system-images;android-36;google_apis_playstore;x86_64;extra',
|
||||
])('rejects malformed --system-image-package %s', async systemImagePackage => {
|
||||
const { command } = createCommand([
|
||||
'--platform',
|
||||
'android',
|
||||
'--non-interactive',
|
||||
'--system-image-package',
|
||||
systemImagePackage,
|
||||
]);
|
||||
|
||||
await expect(command.runAsync()).rejects.toThrow(
|
||||
'Expected an Android SDK package coordinate in the form'
|
||||
);
|
||||
expect(mockCreateDeviceRunSessionAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects a non-x86_64 Android system image', async () => {
|
||||
const { command } = createCommand([
|
||||
'--platform',
|
||||
'android',
|
||||
'--non-interactive',
|
||||
'--system-image-package',
|
||||
'system-images;android-36;google_apis_playstore;arm64-v8a',
|
||||
]);
|
||||
|
||||
await expect(command.runAsync()).rejects.toThrow(
|
||||
'must be x86_64 because EAS Android simulator sessions use x86_64 nested-virtualization runners'
|
||||
);
|
||||
expect(mockCreateDeviceRunSessionAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('omits resourceClass when --resource-class is not set', async () => {
|
||||
|
||||
@@ -54,6 +54,8 @@ const APP_PLATFORM_BY_FLAG_VALUE: Record<PlatformFlagValue, AppPlatform> = {
|
||||
android: AppPlatform.Android,
|
||||
ios: AppPlatform.Ios,
|
||||
};
|
||||
const ANDROID_SYSTEM_IMAGE_PACKAGE_EXAMPLE =
|
||||
'system-images;android-36;google_apis_playstore;x86_64';
|
||||
|
||||
export default class Simulator extends EasCommand {
|
||||
static override hidden = true;
|
||||
@@ -73,7 +75,12 @@ export default class Simulator extends EasCommand {
|
||||
}),
|
||||
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.',
|
||||
'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_9"). Defaults to a device chosen by the runner.',
|
||||
}),
|
||||
'system-image-package': Flags.string({
|
||||
description:
|
||||
`Android SDK system image package to use for the emulator (e.g. "${ANDROID_SYSTEM_IMAGE_PACKAGE_EXAMPLE}"). ` +
|
||||
'Only supported for Android and must use the x86_64 architecture required by EAS nested-virtualization runners.',
|
||||
}),
|
||||
'build-id': Flags.string({
|
||||
description: 'EAS Build to install and launch before the simulator session is ready.',
|
||||
@@ -212,6 +219,10 @@ export default class Simulator extends EasCommand {
|
||||
}
|
||||
|
||||
const platform = await resolvePlatformAsync(flags.platform, nonInteractive);
|
||||
const androidSystemImagePackage = resolveAndroidSystemImagePackage(
|
||||
flags['system-image-package'],
|
||||
platform
|
||||
);
|
||||
const expoGoSdkVersion = flags['expo-go']
|
||||
? await resolveExpoGoSdkVersionAsync({ projectDir, sdkVersion: sdkVersionFromFlag })
|
||||
: undefined;
|
||||
@@ -236,7 +247,17 @@ export default class Simulator extends EasCommand {
|
||||
platform,
|
||||
type: DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE[flags.type],
|
||||
packageVersion: flags['package-version'],
|
||||
deviceIdentifier,
|
||||
...(platform === AppPlatform.Android && (deviceIdentifier || androidSystemImagePackage)
|
||||
? {
|
||||
android: {
|
||||
...(deviceIdentifier ? { deviceIdentifier } : {}),
|
||||
...(androidSystemImagePackage
|
||||
? { systemImagePackage: androidSystemImagePackage }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(platform === AppPlatform.Ios && deviceIdentifier ? { ios: { deviceIdentifier } } : {}),
|
||||
...(buildId ? { buildId } : {}),
|
||||
...(applicationArchiveUrlFromFlag
|
||||
? { applicationArchiveUrl: applicationArchiveUrlFromFlag }
|
||||
@@ -395,6 +416,48 @@ export default class Simulator extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
function resolveAndroidSystemImagePackage(
|
||||
rawSystemImagePackage: string | undefined,
|
||||
platform: AppPlatform
|
||||
): string | undefined {
|
||||
if (rawSystemImagePackage === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const systemImagePackage = rawSystemImagePackage.trim();
|
||||
if (!systemImagePackage) {
|
||||
throw new EasCommandError(
|
||||
`The --system-image-package value cannot be blank. Pass a package such as "${ANDROID_SYSTEM_IMAGE_PACKAGE_EXAMPLE}", or omit the flag to use the default.`
|
||||
);
|
||||
}
|
||||
if (platform !== AppPlatform.Android) {
|
||||
throw new EasCommandError(
|
||||
'The --system-image-package flag is only supported for Android simulator sessions. Use --platform android or omit the flag.'
|
||||
);
|
||||
}
|
||||
|
||||
const [namespace, androidVersion, imageFlavor, architecture, ...extraSegments] =
|
||||
systemImagePackage.split(';');
|
||||
const isValidCoordinate =
|
||||
namespace === 'system-images' &&
|
||||
/^android-[A-Za-z0-9_.-]+$/.test(androidVersion ?? '') &&
|
||||
/^[A-Za-z0-9_.-]+$/.test(imageFlavor ?? '') &&
|
||||
architecture !== undefined &&
|
||||
extraSegments.length === 0;
|
||||
if (!isValidCoordinate) {
|
||||
throw new EasCommandError(
|
||||
`Invalid --system-image-package "${systemImagePackage}". Expected an Android SDK package coordinate in the form "system-images;android-<API>;image-flavor;x86_64", for example "${ANDROID_SYSTEM_IMAGE_PACKAGE_EXAMPLE}".`
|
||||
);
|
||||
}
|
||||
if (architecture !== 'x86_64') {
|
||||
throw new EasCommandError(
|
||||
`The --system-image-package architecture must be x86_64 because EAS Android simulator sessions use x86_64 nested-virtualization runners; received "${architecture}".`
|
||||
);
|
||||
}
|
||||
|
||||
return systemImagePackage;
|
||||
}
|
||||
|
||||
async function resolvePlatformAsync(
|
||||
platform: PlatformFlagValue | undefined,
|
||||
nonInteractive: boolean
|
||||
|
||||
@@ -3,13 +3,26 @@ import gql from 'graphql-tag';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
CreateDeviceRunSessionInput,
|
||||
CreateDeviceRunSessionMutation,
|
||||
CreateDeviceRunSessionMutationVariables,
|
||||
EnsureDeviceRunSessionStoppedMutation,
|
||||
EnsureDeviceRunSessionStoppedMutationVariables,
|
||||
CreateDeviceRunSessionInput as GeneratedCreateDeviceRunSessionInput,
|
||||
} from '../generated';
|
||||
|
||||
export type CreateDeviceRunSessionInput = Omit<
|
||||
GeneratedCreateDeviceRunSessionInput,
|
||||
'deviceIdentifier'
|
||||
> & {
|
||||
android?: {
|
||||
deviceIdentifier?: string;
|
||||
systemImagePackage?: string;
|
||||
};
|
||||
ios?: {
|
||||
deviceIdentifier?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const DeviceRunSessionMutation = {
|
||||
async createDeviceRunSessionAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
|
||||
Reference in new issue
Block a user