@expo/build-tools 23.2.0 → 24.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/context.d.ts CHANGED
@@ -48,6 +48,7 @@ export interface BuildContextOptions {
48
48
  skipNativeBuild?: boolean;
49
49
  metadata?: Metadata;
50
50
  expoApiV2BaseUrl?: string;
51
+ mcpServerUrl?: string;
51
52
  }
52
53
  export declare class SkipNativeBuildError extends Error {
53
54
  }
@@ -62,6 +63,7 @@ export declare class BuildContext<TJob extends Job = Job> {
62
63
  }) => void;
63
64
  readonly skipNativeBuild?: boolean;
64
65
  readonly expoApiV2BaseUrl?: string;
66
+ readonly mcpServerUrl?: string;
65
67
  artifacts: Artifacts;
66
68
  private readonly _isLocal;
67
69
  private _env;
package/dist/context.js CHANGED
@@ -25,6 +25,7 @@ class BuildContext {
25
25
  reportError;
26
26
  skipNativeBuild;
27
27
  expoApiV2BaseUrl;
28
+ mcpServerUrl;
28
29
  artifacts = {};
29
30
  _isLocal;
30
31
  _env;
@@ -49,6 +50,7 @@ class BuildContext {
49
50
  this._metadata = options.metadata;
50
51
  this.skipNativeBuild = options.skipNativeBuild;
51
52
  this.expoApiV2BaseUrl = options.expoApiV2BaseUrl;
53
+ this.mcpServerUrl = options.mcpServerUrl;
52
54
  const environmentSecrets = this.getEnvironmentSecrets(job);
53
55
  this._env = {
54
56
  ...options.env,
@@ -23,6 +23,7 @@ export declare class CustomBuildContext<TJob extends Job = Job> implements Exter
23
23
  readonly logger: bunyan;
24
24
  readonly graphqlClient: Client;
25
25
  readonly runtimeApi: BuilderRuntimeApi;
26
+ readonly mcpServerUrl?: string;
26
27
  job: TJob;
27
28
  metadata?: Metadata;
28
29
  private _env;
@@ -38,6 +38,7 @@ class CustomBuildContext {
38
38
  logger;
39
39
  graphqlClient;
40
40
  runtimeApi;
41
+ mcpServerUrl;
41
42
  job;
42
43
  metadata;
43
44
  _env;
@@ -56,6 +57,7 @@ class CustomBuildContext {
56
57
  this.runtimeApi = {
57
58
  uploadArtifact: (...args) => buildCtx['uploadArtifact'](...args),
58
59
  };
60
+ this.mcpServerUrl = buildCtx.mcpServerUrl;
59
61
  this.expoApiV2BaseUrl = buildCtx.expoApiV2BaseUrl;
60
62
  this.startTime = new Date();
61
63
  }
@@ -39,6 +39,21 @@ function createStartAndroidEmulatorBuildFunction() {
39
39
  defaultValue: AndroidEmulatorUtils_1.AndroidEmulatorUtils.defaultSystemImagePackage,
40
40
  allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
41
41
  }),
42
+ steps_1.BuildStepInput.createProvider({
43
+ id: 'lcd_width',
44
+ required: false,
45
+ allowedValueTypeName: steps_1.BuildStepInputValueTypeName.NUMBER,
46
+ }),
47
+ steps_1.BuildStepInput.createProvider({
48
+ id: 'lcd_height',
49
+ required: false,
50
+ allowedValueTypeName: steps_1.BuildStepInputValueTypeName.NUMBER,
51
+ }),
52
+ steps_1.BuildStepInput.createProvider({
53
+ id: 'lcd_density',
54
+ required: false,
55
+ allowedValueTypeName: steps_1.BuildStepInputValueTypeName.NUMBER,
56
+ }),
42
57
  steps_1.BuildStepInput.createProvider({
43
58
  id: 'count',
44
59
  required: false,
@@ -75,6 +90,9 @@ function createStartAndroidEmulatorBuildFunction() {
75
90
  const systemImagePackage = `${inputs.system_image_package.value}`;
76
91
  // We can cast because allowedValueTypeName validated this is a string.
77
92
  const deviceIdentifier = inputs.device_identifier.value;
93
+ const lcdWidth = inputs.lcd_width.value;
94
+ const lcdHeight = inputs.lcd_height.value;
95
+ const lcdDensity = inputs.lcd_density.value;
78
96
  const shouldAdjustAnimationScale = env.ANDROID_EMULATOR_ADJUST_ANIMATION_SCALE !== 'false' &&
79
97
  env.ANDROID_EMULATOR_ADJUST_ANIMATION_SCALE !== '0';
80
98
  if (!shouldAdjustAnimationScale) {
@@ -107,6 +125,9 @@ function createStartAndroidEmulatorBuildFunction() {
107
125
  deviceName,
108
126
  systemImagePackage,
109
127
  deviceIdentifier: deviceIdentifier ?? null,
128
+ lcdWidth: lcdWidth ?? null,
129
+ lcdHeight: lcdHeight ?? null,
130
+ lcdDensity: lcdDensity ?? null,
110
131
  env,
111
132
  logger,
112
133
  });
@@ -82,21 +82,28 @@ function createStartArgentRemoteSessionBuildFunction(ctx) {
82
82
  if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
83
83
  await (0, remoteDeviceRunSession_1.selectXcodeDeveloperDirectoryAsync)({ env, logger });
84
84
  }
85
- // Argent shells out to ffmpeg to record the screen. Installing it pulls a
86
- // large Homebrew dependency tree, so do not block session readiness on it:
87
- // the session comes up at its usual speed and only a recording started in
88
- // the first moments misses ffmpeg. Never rejects, so `void` is safe.
89
- void (0, remoteDeviceRunSession_1.ensureFfmpegInstalledAsync)({ runtimePlatform, env, logger });
85
+ // Start the potentially slow installation while Argent is being prepared.
86
+ // On Linux expo-device-hub calls this again and awaits the same in-flight
87
+ // setup before launching. On macOS this remains non-blocking, so only a
88
+ // recording started in the first moments may miss ffmpeg.
89
+ // Never rejects, so `void` is safe.
90
+ void (0, remoteDeviceRunSession_1.ensureFfmpegInstalledOnceAsync)({ runtimePlatform, env, logger });
90
91
  logger.info('Enabling the Argent artifacts list endpoint flag.');
91
- await (0, turtle_spawn_1.default)('bunx', [`${ARGENT_PACKAGE_NAME}@${versionSpec}`, 'enable', ARGENT_ARTIFACTS_LIST_ENDPOINT_FLAG], { env, logger });
92
+ await (0, turtle_spawn_1.default)('bun', [
93
+ 'x',
94
+ `${ARGENT_PACKAGE_NAME}@${versionSpec}`,
95
+ 'enable',
96
+ ARGENT_ARTIFACTS_LIST_ENDPOINT_FLAG,
97
+ ], { env, logger });
92
98
  logger.info('Enabling the Argent tool-server event log flag.');
93
- await (0, turtle_spawn_1.default)('bunx', [`${ARGENT_PACKAGE_NAME}@${versionSpec}`, 'enable', ARGENT_EVENT_LOG_FLAG], { env, logger });
94
- logger.info(`Launching ${ARGENT_PACKAGE_NAME}@${versionSpec} tool-server via bunx.`);
95
- // Keep Argent itself in foreground mode under the detached bunx process. This preserves
96
- // the bunx -> Argent CLI -> tool-server ancestry used to identify the matching state file.
99
+ await (0, turtle_spawn_1.default)('bun', ['x', `${ARGENT_PACKAGE_NAME}@${versionSpec}`, 'enable', ARGENT_EVENT_LOG_FLAG], { env, logger });
100
+ logger.info(`Launching ${ARGENT_PACKAGE_NAME}@${versionSpec} tool-server via bun x.`);
101
+ // Keep Argent itself in foreground mode under the detached bun process. This preserves
102
+ // the bun -> Argent CLI -> tool-server ancestry used to identify the matching state file.
97
103
  const argentServer = (0, remoteDeviceRunSession_1.spawnDetached)({
98
- command: 'bunx',
104
+ command: 'bun',
99
105
  args: [
106
+ 'x',
100
107
  `${ARGENT_PACKAGE_NAME}@${versionSpec}`,
101
108
  'server',
102
109
  'start',
@@ -234,7 +241,7 @@ function warnIfArgentPackageVersionCannotBeVerified({ packageVersion, logger, })
234
241
  if (!validVersion) {
235
242
  logger.warn(`Argent remote simulator sessions require ${ARGENT_PACKAGE_NAME}@${exports.MIN_ARGENT_REMOTE_SESSION_VERSION} or newer, ` +
236
243
  `but package_version "${packageVersion}" is not an exact semver version that EAS can verify. ` +
237
- `Continuing and letting bunx resolve it.`);
244
+ `Continuing and letting bun x resolve it.`);
238
245
  return;
239
246
  }
240
247
  if (semver_1.default.lt(validVersion, exports.MIN_ARGENT_REMOTE_SESSION_VERSION)) {
@@ -124,18 +124,24 @@ function createUploadToAscBuildFunction() {
124
124
  const ascApiKeyJson = await fs_extra_1.default.readJson(ascApiKeyPath);
125
125
  const ascApiKey = zod_1.z
126
126
  .object({
127
- issuer_id: zod_1.z.string(),
127
+ issuer_id: zod_1.z.string().nullish(),
128
128
  key_id: zod_1.z.string(),
129
129
  key: zod_1.z.string(),
130
130
  })
131
131
  .parse(ascApiKeyJson);
132
132
  const privateKey = await jose.importPKCS8(ascApiKey.key, 'ES256');
133
- const token = await new jose.SignJWT({})
133
+ const jwt = new jose.SignJWT({})
134
134
  .setProtectedHeader({ alg: 'ES256', kid: ascApiKey.key_id })
135
- .setIssuer(ascApiKey.issuer_id)
136
135
  .setAudience('appstoreconnect-v1')
137
- .setExpirationTime('20m')
138
- .sign(privateKey);
136
+ .setExpirationTime('20m');
137
+ if (ascApiKey.issuer_id) {
138
+ jwt.setIssuer(ascApiKey.issuer_id);
139
+ }
140
+ else {
141
+ // Nullish issuer_id means an individual API key
142
+ jwt.setSubject('user');
143
+ }
144
+ const token = await jwt.sign(privateKey);
139
145
  const client = new AscApiClient_1.AscApiClient({ token, logger: stepsCtx.logger });
140
146
  stepsCtx.logger.info(`Reading App information for Apple app identifier: ${appleAppIdentifier}...`);
141
147
  const appResponse = await AscApiUtils_1.AscApiUtils.getAppInfoAsync({ client, appleAppIdentifier });
@@ -350,6 +350,7 @@ exports.APPIUM_COMMAND_SUMMARIES = {
350
350
  mobileSimctl: 'Ran a simctl command',
351
351
  mobileSiriCommand: 'Sent a Siri command',
352
352
  mobileStartActivity: 'Started an activity',
353
+ mobileStartAutomationSession: 'Started an automation session',
353
354
  mobileStartLogsBroadcast: 'Started log streaming',
354
355
  mobileStartMediaProjectionRecording: 'Started media projection recording',
355
356
  mobileStartNetworkMonitor: 'Started network monitoring',
@@ -359,6 +360,7 @@ exports.APPIUM_COMMAND_SUMMARIES = {
359
360
  mobileStartService: 'Started a service',
360
361
  mobileStartSystemMonitor: 'Started system monitoring',
361
362
  mobileStartXctestScreenRecording: 'Started XCTest screen recording',
363
+ mobileStopAutomationSession: 'Stopped an automation session',
362
364
  mobileStopLogsBroadcast: 'Stopped log streaming',
363
365
  mobileStopMediaProjectionRecording: 'Stopped media projection recording',
364
366
  mobileStopNetworkMonitor: 'Stopped network monitoring',
@@ -380,6 +382,7 @@ exports.APPIUM_COMMAND_SUMMARIES = {
380
382
  mobileUnscheduleAction: 'Canceled a scheduled action',
381
383
  mobileUpdateSafariPreferences: 'Updated Safari preferences',
382
384
  mobileViewPortRect: 'Read the viewport size',
385
+ mobileViewportElementRect: 'Read an element position and size in the viewport',
383
386
  mobileViewportScreenshot: 'Took a viewport screenshot',
384
387
  mobileVoiceOverCurrentSpeech: 'Read current VoiceOver speech',
385
388
  mobileVoiceOverMove: 'Moved the VoiceOver cursor',
@@ -1,4 +1,4 @@
1
1
  export type BaseDriverAppiumCommand = 'activateApp' | 'activateIMEEngine' | 'active' | 'addAuthCredential' | 'addVirtualAuthenticator' | 'availableIMEEngines' | 'back' | 'clear' | 'clearDevicePosture' | 'click' | 'closeWindow' | 'createNewWindow' | 'createSession' | 'createVirtualPressureSource' | 'createVirtualSensor' | 'deactivateIMEEngine' | 'deleteCookie' | 'deleteCookies' | 'deleteSession' | 'deleteVirtualPressureSource' | 'deleteVirtualSensor' | 'elementDisplayed' | 'elementEnabled' | 'elementSelected' | 'elementShadowRoot' | 'execute' | 'executeAsync' | 'executeCdp' | 'fedCMCancelDialog' | 'fedCMClickDialogButton' | 'fedCMGetAccounts' | 'fedCMGetDialogType' | 'fedCMGetTitle' | 'fedCMResetCooldown' | 'fedCMSelectAccount' | 'fedCMSetDelayEnabled' | 'findElement' | 'findElementFromElement' | 'findElementFromShadowRoot' | 'findElements' | 'findElementsFromElement' | 'findElementsFromShadowRoot' | 'forward' | 'fullScreenWindow' | 'generateTestReport' | 'getActiveIMEEngine' | 'getAlertText' | 'getAppiumSessionCapabilities' | 'getAppiumSessions' | 'getAttribute' | 'getAuthCredential' | 'getComputedLabel' | 'getComputedRole' | 'getContexts' | 'getCookie' | 'getCookies' | 'getCssProperty' | 'getCurrentContext' | 'getDeviceTime' | 'getElementRect' | 'getElementScreenshot' | 'getGeoLocation' | 'getGlobalPrivacyControl' | 'getLog' | 'getLogEvents' | 'getLogTypes' | 'getName' | 'getNetworkConnection' | 'getOrientation' | 'getPageSource' | 'getProperty' | 'getRotation' | 'getScreenshot' | 'getSession' | 'getSettings' | 'getStatus' | 'getText' | 'getTimeouts' | 'getUrl' | 'getVirtualSensorInfo' | 'getWindowHandle' | 'getWindowHandles' | 'getWindowRect' | 'hideKeyboard' | 'installApp' | 'isAppInstalled' | 'isIMEActivated' | 'isKeyboardShown' | 'listCommands' | 'listExtensions' | 'logCustomEvent' | 'maximizeWindow' | 'minimizeWindow' | 'performActions' | 'postAcceptAlert' | 'postDismissAlert' | 'printPage' | 'pullFile' | 'pullFolder' | 'pushFile' | 'queryAppState' | 'receiveAsyncResponse' | 'refresh' | 'releaseActions' | 'removeAllAuthCredentials' | 'removeApp' | 'removeAuthCredential' | 'removeVirtualAuthenticator' | 'setAlertText' | 'setContext' | 'setCookie' | 'setDevicePosture' | 'setFrame' | 'setGeoLocation' | 'setGlobalPrivacyControl' | 'setNetworkConnection' | 'setOrientation' | 'setPermissions' | 'setRPHRegistrationMode' | 'setRotation' | 'setSPCTransactionMode' | 'setStorageAccess' | 'setUrl' | 'setUserAuthVerified' | 'setValue' | 'setWindow' | 'setWindowRect' | 'switchToParentFrame' | 'terminateApp' | 'timeouts' | 'title' | 'updateSettings' | 'updateVirtualPressureSource' | 'updateVirtualSensorReading';
2
- export type XCUITestAppiumCommand = 'asyncScriptTimeout' | 'background' | 'closeApp' | 'disableConditionInducer' | 'enableConditionInducer' | 'getClipboard' | 'getLocation' | 'getLocationInView' | 'getScreenInfo' | 'getSize' | 'getStrings' | 'getViewportRect' | 'getViewportScreenshot' | 'getWindowSize' | 'implicitWait' | 'isLocked' | 'keys' | 'launchApp' | 'listConditionInducers' | 'lock' | 'mobileActivateApp' | 'mobileCalibrateWebToRealCoordinatesTranslation' | 'mobileClearApp' | 'mobileClearKeychains' | 'mobileConfigureLocalization' | 'mobileDeepLink' | 'mobileDeleteFile' | 'mobileDeleteFolder' | 'mobileDisableVoiceOver' | 'mobileDoubleTap' | 'mobileDragFromToForDuration' | 'mobileDragFromToWithVelocity' | 'mobileEnableVoiceOver' | 'mobileEnrollBiometric' | 'mobileExpectNotification' | 'mobileForcePress' | 'mobileGetActiveAppInfo' | 'mobileGetAppearance' | 'mobileGetBatteryInfo' | 'mobileGetContentSize' | 'mobileGetContexts' | 'mobileGetDeviceInfo' | 'mobileGetDeviceTime' | 'mobileGetIncreaseContrast' | 'mobileGetPasteboard' | 'mobileGetPermission' | 'mobileGetSimulatedLocation' | 'mobileGetSource' | 'mobileGetXctestScreenRecordingInfo' | 'mobileHandleAlert' | 'mobileHideKeyboard' | 'mobileInstallApp' | 'mobileInstallCertificate' | 'mobileInstallXCTestBundle' | 'mobileIsAppInstalled' | 'mobileIsBiometricEnrolled' | 'mobileIsVoiceOverEnabled' | 'mobileKeys' | 'mobileKillApp' | 'mobileLaunchApp' | 'mobileListApps' | 'mobileListCertificates' | 'mobileListXCTestBundles' | 'mobilePerformAccessibilityAudit' | 'mobilePerformHandGesture' | 'mobilePerformIndigoHidEvent' | 'mobilePerformIoHidEvent' | 'mobilePinch' | 'mobilePressButton' | 'mobilePullFile' | 'mobilePullFolder' | 'mobilePushFile' | 'mobilePushNotification' | 'mobileQueryAppState' | 'mobileRemoveApp' | 'mobileRemoveCertificate' | 'mobileResetLocationService' | 'mobileResetPermission' | 'mobileResetSimulatedLocation' | 'mobileRotateDigitalCrown' | 'mobileRotateElement' | 'mobileRunXCTest' | 'mobileScroll' | 'mobileScrollToElement' | 'mobileSelectPickerWheelValue' | 'mobileSendBiometricMatch' | 'mobileSendMemoryWarning' | 'mobileSetAppearance' | 'mobileSetContentSize' | 'mobileSetIncreaseContrast' | 'mobileSetPasteboard' | 'mobileSetPermissions' | 'mobileSetSimulatedLocation' | 'mobileShake' | 'mobileSimctl' | 'mobileSiriCommand' | 'mobileStartLogsBroadcast' | 'mobileStartNetworkMonitor' | 'mobileStartPerfRecord' | 'mobileStartScreenRecording' | 'mobileStartSystemMonitor' | 'mobileStartXctestScreenRecording' | 'mobileStopLogsBroadcast' | 'mobileStopNetworkMonitor' | 'mobileStopPerfRecord' | 'mobileStopScreenRecording' | 'mobileStopSystemMonitor' | 'mobileStopXctestScreenRecording' | 'mobileSwipe' | 'mobileTap' | 'mobileTapWithNumberOfTaps' | 'mobileTerminateApp' | 'mobileTouchAndHold' | 'mobileTwoFingerTap' | 'mobileUpdateSafariPreferences' | 'mobileVoiceOverCurrentSpeech' | 'mobileVoiceOverMove' | 'reset' | 'setClipboard' | 'setValueImmediate' | 'startAudioRecording' | 'startRecordingScreen' | 'stopAudioRecording' | 'stopRecordingScreen' | 'submit' | 'toggleEnrollTouchId' | 'touchId' | 'unlock';
3
- export type UiAutomator2AppiumCommand = 'background' | 'fingerprint' | 'getClipboard' | 'getCurrentActivity' | 'getCurrentPackage' | 'getDisplayDensity' | 'getLocation' | 'getLocationInView' | 'getPerformanceData' | 'getPerformanceDataTypes' | 'getSize' | 'getStrings' | 'getSystemBars' | 'getWindowSize' | 'gsmCall' | 'gsmSignal' | 'gsmVoice' | 'implicitWait' | 'isLocationServicesEnabled' | 'isLocked' | 'keyevent' | 'keys' | 'lock' | 'longPressKeyCode' | 'mobileAcceptAlert' | 'mobileBackgroundApp' | 'mobileBluetooth' | 'mobileBroadcast' | 'mobileChangePermissions' | 'mobileClearApp' | 'mobileClickGesture' | 'mobileDeepLink' | 'mobileDeleteFile' | 'mobileDeviceidle' | 'mobileDismissAlert' | 'mobileDoubleClickGesture' | 'mobileDragGesture' | 'mobileExecEmuConsoleCommand' | 'mobileFingerprint' | 'mobileFlingGesture' | 'mobileGetActionHistory' | 'mobileGetBatteryInfo' | 'mobileGetChromeCapabilities' | 'mobileGetConnectivity' | 'mobileGetContexts' | 'mobileGetDeclaredOrientation' | 'mobileGetDeviceInfo' | 'mobileGetDeviceTime' | 'mobileGetGeolocation' | 'mobileGetNotifications' | 'mobileGetPerformanceData' | 'mobileGetPermissions' | 'mobileGetUiMode' | 'mobileGsmCall' | 'mobileGsmSignal' | 'mobileGsmVoice' | 'mobileInjectEmulatorCameraImage' | 'mobileInstallApp' | 'mobileInstallMultipleApks' | 'mobileIsAppInstalled' | 'mobileIsMediaProjectionRecordingRunning' | 'mobileListApps' | 'mobileListDisplays' | 'mobileListSms' | 'mobileListWindows' | 'mobileLongClickGesture' | 'mobileNetworkSpeed' | 'mobileNfc' | 'mobilePerformEditorAction' | 'mobilePerformStatusBarCommand' | 'mobilePinchCloseGesture' | 'mobilePinchOpenGesture' | 'mobilePowerAc' | 'mobilePowerCapacity' | 'mobilePressKey' | 'mobileRefreshGpsCache' | 'mobileRemoveApp' | 'mobileReplaceElementValue' | 'mobileResetAccessibilityCache' | 'mobileResetGeolocation' | 'mobileScheduleAction' | 'mobileScreenshots' | 'mobileScroll' | 'mobileScrollBackTo' | 'mobileScrollGesture' | 'mobileSendSms' | 'mobileSendTrimMemory' | 'mobileSetConnectivity' | 'mobileSetGeolocation' | 'mobileSetUiMode' | 'mobileShell' | 'mobileStartActivity' | 'mobileStartLogsBroadcast' | 'mobileStartMediaProjectionRecording' | 'mobileStartScreenStreaming' | 'mobileStartService' | 'mobileStopLogsBroadcast' | 'mobileStopMediaProjectionRecording' | 'mobileStopScreenStreaming' | 'mobileStopService' | 'mobileSwipeGesture' | 'mobileTerminateApp' | 'mobileType' | 'mobileUnlock' | 'mobileUnscheduleAction' | 'mobileViewPortRect' | 'mobileViewportScreenshot' | 'networkSpeed' | 'openNotifications' | 'powerAC' | 'powerCapacity' | 'pressKeyCode' | 'replaceValue' | 'sendSMS' | 'sensorSet' | 'setClipboard' | 'setStylusHandwriting' | 'setValueImmediate' | 'startActivity' | 'startRecordingScreen' | 'stopRecordingScreen' | 'toggleData' | 'toggleFlightMode' | 'toggleLocationServices' | 'toggleWiFi' | 'unlock';
2
+ export type XCUITestAppiumCommand = 'asyncScriptTimeout' | 'background' | 'closeApp' | 'disableConditionInducer' | 'enableConditionInducer' | 'getClipboard' | 'getLocation' | 'getLocationInView' | 'getScreenInfo' | 'getSize' | 'getStrings' | 'getViewportRect' | 'getViewportScreenshot' | 'getWindowSize' | 'implicitWait' | 'isLocked' | 'keys' | 'launchApp' | 'listConditionInducers' | 'lock' | 'mobileActivateApp' | 'mobileCalibrateWebToRealCoordinatesTranslation' | 'mobileClearApp' | 'mobileClearKeychains' | 'mobileConfigureLocalization' | 'mobileDeepLink' | 'mobileDeleteFile' | 'mobileDeleteFolder' | 'mobileDisableVoiceOver' | 'mobileDoubleTap' | 'mobileDragFromToForDuration' | 'mobileDragFromToWithVelocity' | 'mobileEnableVoiceOver' | 'mobileEnrollBiometric' | 'mobileExpectNotification' | 'mobileForcePress' | 'mobileGetActiveAppInfo' | 'mobileGetAppearance' | 'mobileGetBatteryInfo' | 'mobileGetContentSize' | 'mobileGetContexts' | 'mobileGetDeviceInfo' | 'mobileGetDeviceTime' | 'mobileGetIncreaseContrast' | 'mobileGetPasteboard' | 'mobileGetPermission' | 'mobileGetSimulatedLocation' | 'mobileGetSource' | 'mobileGetXctestScreenRecordingInfo' | 'mobileHandleAlert' | 'mobileHideKeyboard' | 'mobileInstallApp' | 'mobileInstallCertificate' | 'mobileInstallXCTestBundle' | 'mobileIsAppInstalled' | 'mobileIsBiometricEnrolled' | 'mobileIsVoiceOverEnabled' | 'mobileKeys' | 'mobileKillApp' | 'mobileLaunchApp' | 'mobileListApps' | 'mobileListCertificates' | 'mobileListXCTestBundles' | 'mobilePerformAccessibilityAudit' | 'mobilePerformHandGesture' | 'mobilePerformIndigoHidEvent' | 'mobilePerformIoHidEvent' | 'mobilePinch' | 'mobilePressButton' | 'mobilePullFile' | 'mobilePullFolder' | 'mobilePushFile' | 'mobilePushNotification' | 'mobileQueryAppState' | 'mobileRemoveApp' | 'mobileRemoveCertificate' | 'mobileResetLocationService' | 'mobileResetPermission' | 'mobileResetSimulatedLocation' | 'mobileRotateDigitalCrown' | 'mobileRotateElement' | 'mobileRunXCTest' | 'mobileScroll' | 'mobileScrollToElement' | 'mobileSelectPickerWheelValue' | 'mobileSendBiometricMatch' | 'mobileSendMemoryWarning' | 'mobileSetAppearance' | 'mobileSetContentSize' | 'mobileSetIncreaseContrast' | 'mobileSetPasteboard' | 'mobileSetPermissions' | 'mobileSetSimulatedLocation' | 'mobileShake' | 'mobileSimctl' | 'mobileSiriCommand' | 'mobileStartAutomationSession' | 'mobileStartLogsBroadcast' | 'mobileStartNetworkMonitor' | 'mobileStartPerfRecord' | 'mobileStartScreenRecording' | 'mobileStartSystemMonitor' | 'mobileStartXctestScreenRecording' | 'mobileStopAutomationSession' | 'mobileStopLogsBroadcast' | 'mobileStopNetworkMonitor' | 'mobileStopPerfRecord' | 'mobileStopScreenRecording' | 'mobileStopSystemMonitor' | 'mobileStopXctestScreenRecording' | 'mobileSwipe' | 'mobileTap' | 'mobileTapWithNumberOfTaps' | 'mobileTerminateApp' | 'mobileTouchAndHold' | 'mobileTwoFingerTap' | 'mobileUpdateSafariPreferences' | 'mobileVoiceOverCurrentSpeech' | 'mobileVoiceOverMove' | 'reset' | 'setClipboard' | 'setValueImmediate' | 'startAudioRecording' | 'startRecordingScreen' | 'stopAudioRecording' | 'stopRecordingScreen' | 'submit' | 'toggleEnrollTouchId' | 'touchId' | 'unlock';
3
+ export type UiAutomator2AppiumCommand = 'background' | 'fingerprint' | 'getClipboard' | 'getCurrentActivity' | 'getCurrentPackage' | 'getDisplayDensity' | 'getLocation' | 'getLocationInView' | 'getPerformanceData' | 'getPerformanceDataTypes' | 'getSize' | 'getStrings' | 'getSystemBars' | 'getWindowSize' | 'gsmCall' | 'gsmSignal' | 'gsmVoice' | 'implicitWait' | 'isLocationServicesEnabled' | 'isLocked' | 'keyevent' | 'keys' | 'lock' | 'longPressKeyCode' | 'mobileAcceptAlert' | 'mobileBackgroundApp' | 'mobileBluetooth' | 'mobileBroadcast' | 'mobileChangePermissions' | 'mobileClearApp' | 'mobileClickGesture' | 'mobileDeepLink' | 'mobileDeleteFile' | 'mobileDeviceidle' | 'mobileDismissAlert' | 'mobileDoubleClickGesture' | 'mobileDragGesture' | 'mobileExecEmuConsoleCommand' | 'mobileFingerprint' | 'mobileFlingGesture' | 'mobileGetActionHistory' | 'mobileGetBatteryInfo' | 'mobileGetChromeCapabilities' | 'mobileGetConnectivity' | 'mobileGetContexts' | 'mobileGetDeclaredOrientation' | 'mobileGetDeviceInfo' | 'mobileGetDeviceTime' | 'mobileGetGeolocation' | 'mobileGetNotifications' | 'mobileGetPerformanceData' | 'mobileGetPermissions' | 'mobileGetUiMode' | 'mobileGsmCall' | 'mobileGsmSignal' | 'mobileGsmVoice' | 'mobileInjectEmulatorCameraImage' | 'mobileInstallApp' | 'mobileInstallMultipleApks' | 'mobileIsAppInstalled' | 'mobileIsMediaProjectionRecordingRunning' | 'mobileListApps' | 'mobileListDisplays' | 'mobileListSms' | 'mobileListWindows' | 'mobileLongClickGesture' | 'mobileNetworkSpeed' | 'mobileNfc' | 'mobilePerformEditorAction' | 'mobilePerformStatusBarCommand' | 'mobilePinchCloseGesture' | 'mobilePinchOpenGesture' | 'mobilePowerAc' | 'mobilePowerCapacity' | 'mobilePressKey' | 'mobileRefreshGpsCache' | 'mobileRemoveApp' | 'mobileReplaceElementValue' | 'mobileResetAccessibilityCache' | 'mobileResetGeolocation' | 'mobileScheduleAction' | 'mobileScreenshots' | 'mobileScroll' | 'mobileScrollBackTo' | 'mobileScrollGesture' | 'mobileSendSms' | 'mobileSendTrimMemory' | 'mobileSetConnectivity' | 'mobileSetGeolocation' | 'mobileSetUiMode' | 'mobileShell' | 'mobileStartActivity' | 'mobileStartLogsBroadcast' | 'mobileStartMediaProjectionRecording' | 'mobileStartScreenStreaming' | 'mobileStartService' | 'mobileStopLogsBroadcast' | 'mobileStopMediaProjectionRecording' | 'mobileStopScreenStreaming' | 'mobileStopService' | 'mobileSwipeGesture' | 'mobileTerminateApp' | 'mobileType' | 'mobileUnlock' | 'mobileUnscheduleAction' | 'mobileViewPortRect' | 'mobileViewportElementRect' | 'mobileViewportScreenshot' | 'networkSpeed' | 'openNotifications' | 'powerAC' | 'powerCapacity' | 'pressKeyCode' | 'replaceValue' | 'sendSMS' | 'sensorSet' | 'setClipboard' | 'setStylusHandwriting' | 'setValueImmediate' | 'startActivity' | 'startRecordingScreen' | 'stopRecordingScreen' | 'toggleData' | 'toggleFlightMode' | 'toggleLocationServices' | 'toggleWiFi' | 'unlock';
4
4
  export type AppiumCommand = BaseDriverAppiumCommand | XCUITestAppiumCommand | UiAutomator2AppiumCommand;
@@ -33,22 +33,7 @@ export declare function waitForDeviceRunSessionStoppedAsync({ ctx, deviceRunSess
33
33
  signal?: AbortSignal;
34
34
  idleTimeout?: DeviceRunSessionIdleTimeout;
35
35
  }): Promise<void>;
36
- /**
37
- * Install ffmpeg when the runtime does not already provide it, so argent's
38
- * `screen-recording-start` tool can encode a video. The worker images do not
39
- * ship ffmpeg yet, so without this the tool fails with "`ffmpeg` was not found
40
- * on PATH" — on macOS (iOS simulators) and Linux (Android emulators) alike.
41
- *
42
- * Best-effort by design: screen recording is one optional argent tool, so a
43
- * failure here is logged and the session continues without it.
44
- *
45
- * The whole body is wrapped because the caller runs this in the background with
46
- * `void`. There is no unhandledRejection handler in the worker, so a rejection
47
- * escaping here would crash the process and take the live session with it.
48
- * `spawn` is not an async function and can throw synchronously, which
49
- * `asyncResult` cannot catch — it only wraps an already-created promise.
50
- */
51
- export declare function ensureFfmpegInstalledAsync({ runtimePlatform, env, logger, }: {
36
+ export declare function ensureFfmpegInstalledOnceAsync({ runtimePlatform, env, logger, }: {
52
37
  runtimePlatform: BuildRuntimePlatform;
53
38
  env: BuildStepEnv;
54
39
  logger: bunyan;
@@ -122,7 +107,8 @@ export declare function startServeSimWithTunnelAsync(ctx: CustomBuildContext, {
122
107
  timeoutMs: number;
123
108
  packageVersion?: string;
124
109
  }): Promise<ServeSimPreviewHandle>;
125
- export declare function startExpoDeviceHubWithTunnelAsync(ctx: CustomBuildContext, { baseDomain, env, logger, timeoutMs, packageVersion, }: {
110
+ export declare function startExpoDeviceHubWithTunnelAsync(ctx: CustomBuildContext, { runtimePlatform, baseDomain, env, logger, timeoutMs, packageVersion, }: {
111
+ runtimePlatform: BuildRuntimePlatform;
126
112
  baseDomain: string;
127
113
  env: BuildStepEnv;
128
114
  logger: bunyan;
@@ -41,7 +41,7 @@ exports.getNgrokTunnelDomainOrThrow = getNgrokTunnelDomainOrThrow;
41
41
  exports.getNgrokAuthtokenOrThrow = getNgrokAuthtokenOrThrow;
42
42
  exports.selectXcodeDeveloperDirectoryAsync = selectXcodeDeveloperDirectoryAsync;
43
43
  exports.waitForDeviceRunSessionStoppedAsync = waitForDeviceRunSessionStoppedAsync;
44
- exports.ensureFfmpegInstalledAsync = ensureFfmpegInstalledAsync;
44
+ exports.ensureFfmpegInstalledOnceAsync = ensureFfmpegInstalledOnceAsync;
45
45
  exports.turnIceServersToWebPreviewArgs = turnIceServersToWebPreviewArgs;
46
46
  exports.fetchWebPreviewTurnArgsAsync = fetchWebPreviewTurnArgsAsync;
47
47
  exports.uploadRemoteSessionConfigAsync = uploadRemoteSessionConfigAsync;
@@ -79,7 +79,7 @@ const SERVE_SIM_MJPEG_QUALITY = '0.55';
79
79
  const SERVE_SIM_VIDEO_BITRATE = '6000000';
80
80
  const SERVE_SIM_VIDEO_FPS = '60';
81
81
  const EXPO_DEVICE_HUB_PACKAGE_NAME = 'expo-device-hub';
82
- const EXPO_DEVICE_HUB_MAX_DIMENSION = '1280';
82
+ const EXPO_DEVICE_HUB_MAX_DIMENSION = '960';
83
83
  const EXPO_DEVICE_HUB_VIDEO_BITRATE = '6000000';
84
84
  const EXPO_DEVICE_HUB_VIDEO_FPS = '60';
85
85
  const START_DEVICE_RUN_SESSION_MUTATION = (0, gql_tada_1.graphql)(`
@@ -269,10 +269,9 @@ async function sleepUntilAbortedAsync(timeoutMs, signal) {
269
269
  }
270
270
  }
271
271
  }
272
- // Argent encodes screen recordings by piping simulator frames into `ffmpeg`,
273
- // which it resolves from PATH. The tool-server inherits this step's env, so
274
- // spawning ffmpeg resolves against the same PATH argent will search: it rejects
275
- // with ENOENT when the binary is absent, and running it also proves it works.
272
+ // Device-session tools resolve `ffmpeg` from PATH. Spawning it with the step's
273
+ // environment rejects with ENOENT when the binary is absent, and running it also
274
+ // proves that the installed binary works.
276
275
  async function isFfmpegAvailableAsync(env) {
277
276
  return (await (0, results_1.asyncResult)((0, turtle_spawn_1.default)('ffmpeg', ['-version'], { env }))).ok;
278
277
  }
@@ -290,14 +289,14 @@ async function installFfmpegWithAptAsync({ env, logger, }) {
290
289
  await (0, results_1.asyncResult)((0, turtle_spawn_1.default)('sudo', ['apt-get', 'update'], { env: aptEnv, logger }));
291
290
  await (0, turtle_spawn_1.default)('sudo', ['apt-get', 'install', '-y', 'ffmpeg'], { env: aptEnv, logger });
292
291
  }
292
+ let ffmpegSetupPromise;
293
293
  /**
294
- * Install ffmpeg when the runtime does not already provide it, so argent's
295
- * `screen-recording-start` tool can encode a video. The worker images do not
296
- * ship ffmpeg yet, so without this the tool fails with "`ffmpeg` was not found
297
- * on PATH" — on macOS (iOS simulators) and Linux (Android emulators) alike.
294
+ * Install ffmpeg when the runtime does not already provide it. Device-session
295
+ * tools use it for video encoding on macOS (iOS simulators) and Linux (Android
296
+ * emulators) alike, but the worker images do not ship it yet.
298
297
  *
299
- * Best-effort by design: screen recording is one optional argent tool, so a
300
- * failure here is logged and the session continues without it.
298
+ * Best-effort by design: a failure here is logged and the session continues
299
+ * without FFmpeg-dependent features.
301
300
  *
302
301
  * The whole body is wrapped because the caller runs this in the background with
303
302
  * `void`. There is no unhandledRejection handler in the worker, so a rejection
@@ -312,7 +311,7 @@ async function ensureFfmpegInstalledAsync({ runtimePlatform, env, logger, }) {
312
311
  return;
313
312
  }
314
313
  const isDarwin = runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN;
315
- logger.info(`ffmpeg is not installed, installing it with ${isDarwin ? 'Homebrew' : 'apt'} for argent screen recording.`);
314
+ logger.info(`ffmpeg is not installed, installing it with ${isDarwin ? 'Homebrew' : 'apt'} for the device session.`);
316
315
  if (isDarwin) {
317
316
  await installFfmpegWithHomebrewAsync({ env, logger });
318
317
  }
@@ -323,10 +322,26 @@ async function ensureFfmpegInstalledAsync({ runtimePlatform, env, logger, }) {
323
322
  }
324
323
  catch (err) {
325
324
  const error = err instanceof Error ? err : new Error(String(err));
326
- sentry_1.Sentry.capture('Could not install ffmpeg for argent screen recording', error, {
325
+ sentry_1.Sentry.capture('Could not install ffmpeg for the device session', error, {
327
326
  level: 'warning',
328
327
  });
329
- logger.warn({ err: error }, 'Could not install ffmpeg. Argent screen recording will not work in this session.');
328
+ logger.warn({ err: error }, 'Could not install ffmpeg. FFmpeg-dependent features may not work in this session.');
329
+ }
330
+ }
331
+ async function ensureFfmpegInstalledOnceAsync({ runtimePlatform, env, logger, }) {
332
+ if (ffmpegSetupPromise) {
333
+ await ffmpegSetupPromise;
334
+ return;
335
+ }
336
+ const setupPromise = ensureFfmpegInstalledAsync({ runtimePlatform, env, logger });
337
+ ffmpegSetupPromise = setupPromise;
338
+ try {
339
+ await setupPromise;
340
+ }
341
+ finally {
342
+ if (ffmpegSetupPromise === setupPromise) {
343
+ ffmpegSetupPromise = undefined;
344
+ }
330
345
  }
331
346
  }
332
347
  const TurnIceServersResponseSchema = zod_1.z.object({
@@ -629,7 +644,10 @@ async function startServeSimWithTunnelAsync(ctx, { baseDomain, env, logger, time
629
644
  createArgs: (port, turnArgs) => createServeSimArgs({ port, turnArgs, metricsCorsArgs, packageVersion }),
630
645
  });
631
646
  }
632
- async function startExpoDeviceHubWithTunnelAsync(ctx, { baseDomain, env, logger, timeoutMs, packageVersion, }) {
647
+ async function startExpoDeviceHubWithTunnelAsync(ctx, { runtimePlatform, baseDomain, env, logger, timeoutMs, packageVersion, }) {
648
+ if (runtimePlatform === steps_1.BuildRuntimePlatform.LINUX) {
649
+ await ensureFfmpegInstalledOnceAsync({ runtimePlatform, env, logger });
650
+ }
633
651
  return await startWebPreviewWithTunnelAsync(ctx, {
634
652
  baseDomain,
635
653
  env,
@@ -645,7 +663,7 @@ async function startDeviceWebPreviewWithTunnelAsync(ctx, { runtimePlatform, ...o
645
663
  case steps_1.BuildRuntimePlatform.DARWIN:
646
664
  return await startServeSimWithTunnelAsync(ctx, options);
647
665
  case steps_1.BuildRuntimePlatform.LINUX:
648
- return await startExpoDeviceHubWithTunnelAsync(ctx, options);
666
+ return await startExpoDeviceHubWithTunnelAsync(ctx, { ...options, runtimePlatform });
649
667
  }
650
668
  }
651
669
  async function startNgrokTunnelAsync({ port, subdomainPrefix, baseDomain, authtoken, rewriteHostHeader, logger, }) {
@@ -21,10 +21,13 @@ export declare namespace AndroidEmulatorUtils {
21
21
  deviceName: AndroidVirtualDeviceName;
22
22
  env: NodeJS.ProcessEnv;
23
23
  }): Promise<AndroidDeviceSerialId | null>;
24
- function createAsync({ deviceName, systemImagePackage, deviceIdentifier, env, logger, }: {
24
+ function createAsync({ deviceName, systemImagePackage, deviceIdentifier, lcdWidth, lcdHeight, lcdDensity, env, logger, }: {
25
25
  deviceName: AndroidVirtualDeviceName;
26
26
  systemImagePackage: string;
27
27
  deviceIdentifier: AndroidDeviceName | null;
28
+ lcdWidth: number | null;
29
+ lcdHeight: number | null;
30
+ lcdDensity: number | null;
28
31
  env: NodeJS.ProcessEnv;
29
32
  logger: bunyan;
30
33
  }): Promise<void>;
@@ -68,7 +68,7 @@ var AndroidEmulatorUtils;
68
68
  return null;
69
69
  }
70
70
  AndroidEmulatorUtils.getSerialIdAsync = getSerialIdAsync;
71
- async function createAsync({ deviceName, systemImagePackage, deviceIdentifier, env, logger, }) {
71
+ async function createAsync({ deviceName, systemImagePackage, deviceIdentifier, lcdWidth, lcdHeight, lcdDensity, env, logger, }) {
72
72
  const avdManager = (0, turtle_spawn_1.default)('avdmanager', [
73
73
  'create',
74
74
  'avd',
@@ -121,6 +121,10 @@ var AndroidEmulatorUtils;
121
121
  }
122
122
  }
123
123
  }
124
+ if (lcdWidth !== null && lcdHeight !== null && lcdDensity !== null) {
125
+ logger.info(`Setting screen resolution to ${lcdWidth}x${lcdHeight} and density to ${lcdDensity} ppi.`);
126
+ configIniFileContent = `${configIniFileContent}\nhw.lcd.height=${lcdHeight}\nhw.lcd.width=${lcdWidth}\nhw.lcd.density=${lcdDensity}\n`;
127
+ }
124
128
  const shouldAdjustHeapSize = env.ANDROID_EMULATOR_ADJUST_HEAP_SIZE !== 'false' &&
125
129
  env.ANDROID_EMULATOR_ADJUST_HEAP_SIZE !== '0';
126
130
  if (shouldAdjustHeapSize) {
@@ -0,0 +1,2 @@
1
+ import { type bunyan } from '@expo/logger';
2
+ export declare function withLogPhaseAsync<T>(logger: bunyan, name: string, fn: (logger: bunyan) => Promise<T>): Promise<T>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withLogPhaseAsync = withLogPhaseAsync;
4
+ const eas_build_job_1 = require("@expo/eas-build-job");
5
+ const steps_1 = require("@expo/steps");
6
+ async function withLogPhaseAsync(logger, name, fn) {
7
+ const phaseLogger = logger.child({
8
+ phase: eas_build_job_1.BuildPhase.CUSTOM,
9
+ buildStepId: steps_1.BuildStep.getNewId(),
10
+ buildStepDisplayName: name,
11
+ });
12
+ phaseLogger.info({ marker: eas_build_job_1.LogMarker.START_PHASE }, `Start phase: ${name}`);
13
+ try {
14
+ const result = await fn(phaseLogger);
15
+ phaseLogger.info({ marker: eas_build_job_1.LogMarker.END_PHASE, result: eas_build_job_1.BuildPhaseResult.SUCCESS }, `End phase: ${name}`);
16
+ return result;
17
+ }
18
+ catch (error) {
19
+ phaseLogger.info({ marker: eas_build_job_1.LogMarker.END_PHASE, result: eas_build_job_1.BuildPhaseResult.FAIL }, `End phase: ${name}`);
20
+ throw error;
21
+ }
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/build-tools",
3
- "version": "23.2.0",
3
+ "version": "24.0.0",
4
4
  "bugs": "https://github.com/expo/eas-cli/issues",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Expo <support@expo.io>",
@@ -39,17 +39,17 @@
39
39
  "dependencies": {
40
40
  "@expo/config": "55.0.10",
41
41
  "@expo/config-plugins": "55.0.7",
42
- "@expo/downloader": "23.0.0",
43
- "@expo/eas-build-job": "23.1.0",
42
+ "@expo/downloader": "24.0.0",
43
+ "@expo/eas-build-job": "24.0.0",
44
44
  "@expo/env": "^0.4.0",
45
- "@expo/logger": "23.1.0",
45
+ "@expo/logger": "24.0.0",
46
46
  "@expo/package-manager": "1.9.10",
47
47
  "@expo/plist": "^0.3.5",
48
48
  "@expo/results": "^1.0.0",
49
49
  "@expo/spawn-async": "1.7.2",
50
- "@expo/steps": "23.1.0",
51
- "@expo/template-file": "23.0.0",
52
- "@expo/turtle-spawn": "23.1.0",
50
+ "@expo/steps": "24.0.0",
51
+ "@expo/template-file": "24.0.0",
52
+ "@expo/turtle-spawn": "24.0.0",
53
53
  "@expo/xcpretty": "^4.3.1",
54
54
  "@ngrok/ngrok": "1.7.0",
55
55
  "@sentry/node": "7.77.0",
@@ -102,5 +102,5 @@
102
102
  "typescript": "^5.5.4",
103
103
  "uuid": "^9.0.1"
104
104
  },
105
- "gitHead": "74292d2ace404759ab8936d5aa1369e15d4b4f9d"
105
+ "gitHead": "5409c03d58c50b80e4ec9d5ca40f2fbdfaba9aa0"
106
106
  }