@expo/build-tools 23.1.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 +2 -0
- package/dist/context.js +2 -0
- package/dist/customBuildContext.d.ts +1 -0
- package/dist/customBuildContext.js +2 -0
- package/dist/steps/easFunctions.js +2 -2
- package/dist/steps/functions/startAgentDeviceRemoteSession.js +12 -15
- package/dist/steps/functions/startAndroidEmulator.js +21 -0
- package/dist/steps/functions/startAppiumRemoteSession.js +13 -16
- package/dist/steps/functions/startArgentRemoteSession.js +31 -28
- package/dist/steps/functions/startWebPreviewRemoteSession.d.ts +3 -0
- package/dist/steps/functions/{startServeSimRemoteSession.js → startWebPreviewRemoteSession.js} +14 -11
- package/dist/steps/functions/uploadToAsc.js +11 -5
- package/dist/steps/utils/appiumCommandSummary.js +3 -0
- package/dist/steps/utils/appiumCommands.generated.d.ts +2 -2
- package/dist/steps/utils/remoteDeviceRunSession.d.ts +37 -28
- package/dist/steps/utils/remoteDeviceRunSession.js +137 -51
- package/dist/utils/AndroidEmulatorUtils.d.ts +4 -1
- package/dist/utils/AndroidEmulatorUtils.js +5 -1
- package/dist/utils/logPhase.d.ts +2 -0
- package/dist/utils/logPhase.js +22 -0
- package/package.json +8 -8
- package/dist/steps/functions/startServeSimRemoteSession.d.ts +0 -3
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
|
}
|
|
@@ -49,7 +49,7 @@ const startAppiumRemoteSession_1 = require("./functions/startAppiumRemoteSession
|
|
|
49
49
|
const startCuttlefishDevice_1 = require("./functions/startCuttlefishDevice");
|
|
50
50
|
const startIosSimulator_1 = require("./functions/startIosSimulator");
|
|
51
51
|
const startIosSimulatorRecordings_1 = require("./functions/startIosSimulatorRecordings");
|
|
52
|
-
const
|
|
52
|
+
const startWebPreviewRemoteSession_1 = require("./functions/startWebPreviewRemoteSession");
|
|
53
53
|
const startServeSimMetrics_1 = require("./functions/startServeSimMetrics");
|
|
54
54
|
const collectServeSimMetrics_1 = require("./functions/collectServeSimMetrics");
|
|
55
55
|
const uploadArtifact_1 = require("./functions/uploadArtifact");
|
|
@@ -102,7 +102,7 @@ function getEasFunctions(ctx) {
|
|
|
102
102
|
(0, startIosSimulatorRecordings_1.createStartIosSimulatorRecordingsBuildFunction)(),
|
|
103
103
|
(0, finishIosSimulatorRecordings_1.createFinishIosSimulatorRecordingsBuildFunction)(),
|
|
104
104
|
(0, uploadDeviceRunSessionScreenRecordings_1.createUploadDeviceRunSessionScreenRecordingsBuildFunction)(ctx),
|
|
105
|
-
(0,
|
|
105
|
+
(0, startWebPreviewRemoteSession_1.createStartWebPreviewRemoteSessionBuildFunction)(ctx),
|
|
106
106
|
(0, startServeSimMetrics_1.createStartServeSimMetricsBuildFunction)(),
|
|
107
107
|
(0, collectServeSimMetrics_1.createCollectServeSimMetricsBuildFunction)(ctx),
|
|
108
108
|
(0, installMaestro_1.createInstallMaestroBuildFunction)(),
|
|
@@ -81,27 +81,24 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
|
81
81
|
});
|
|
82
82
|
const agentDeviceRemoteSessionUrl = agentDeviceTunnel.url;
|
|
83
83
|
logger.info(`Tunnel is ready at ${agentDeviceRemoteSessionUrl}.`);
|
|
84
|
-
let
|
|
84
|
+
let webPreview;
|
|
85
85
|
let eventCollection;
|
|
86
86
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
logger.info(`Web preview URL: ${serveSim.previewUrl}`);
|
|
97
|
-
}
|
|
87
|
+
webPreview = await (0, remoteDeviceRunSession_1.startDeviceWebPreviewWithTunnelAsync)(ctx, {
|
|
88
|
+
runtimePlatform,
|
|
89
|
+
baseDomain: ngrokTunnelDomain,
|
|
90
|
+
env,
|
|
91
|
+
logger,
|
|
92
|
+
timeoutMs: STARTUP_TIMEOUT_MS,
|
|
93
|
+
});
|
|
94
|
+
logger.info(`Web preview URL: ${webPreview.previewUrl}`);
|
|
98
95
|
await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
|
|
99
96
|
ctx,
|
|
100
97
|
deviceRunSessionId,
|
|
101
98
|
remoteConfig: {
|
|
102
99
|
agentDeviceRemoteSessionUrl,
|
|
103
100
|
agentDeviceRemoteSessionToken: daemonToken,
|
|
104
|
-
|
|
101
|
+
webPreviewUrl: webPreview.previewUrl,
|
|
105
102
|
},
|
|
106
103
|
logger,
|
|
107
104
|
});
|
|
@@ -132,8 +129,8 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
|
132
129
|
});
|
|
133
130
|
}
|
|
134
131
|
finally {
|
|
135
|
-
if (
|
|
136
|
-
await
|
|
132
|
+
if (webPreview) {
|
|
133
|
+
await webPreview.stopAsync();
|
|
137
134
|
}
|
|
138
135
|
await agentDeviceTunnel.stopAsync();
|
|
139
136
|
if (eventCollection) {
|
|
@@ -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
|
});
|
|
@@ -95,7 +95,7 @@ function createStartAppiumRemoteSessionBuildFunction(ctx) {
|
|
|
95
95
|
logger,
|
|
96
96
|
});
|
|
97
97
|
let appiumTunnel;
|
|
98
|
-
let
|
|
98
|
+
let webPreview;
|
|
99
99
|
try {
|
|
100
100
|
appiumTunnel = await (0, remoteDeviceRunSession_1.startNgrokTunnelAsync)({
|
|
101
101
|
port: APPIUM_PORT,
|
|
@@ -104,18 +104,15 @@ function createStartAppiumRemoteSessionBuildFunction(ctx) {
|
|
|
104
104
|
authtoken: ngrokAuthtoken,
|
|
105
105
|
logger,
|
|
106
106
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
case steps_1.BuildRuntimePlatform.LINUX:
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
107
|
+
// expo-device-hub has no serial-selection flag. Device run session workflows must expose
|
|
108
|
+
// a single booted Android emulator so the Hub and Appium resolve the same device.
|
|
109
|
+
webPreview = await (0, remoteDeviceRunSession_1.startDeviceWebPreviewWithTunnelAsync)(ctx, {
|
|
110
|
+
runtimePlatform,
|
|
111
|
+
baseDomain: ngrokTunnelDomain,
|
|
112
|
+
env,
|
|
113
|
+
logger,
|
|
114
|
+
timeoutMs: APPIUM_STARTUP_TIMEOUT_MS,
|
|
115
|
+
});
|
|
119
116
|
await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
|
|
120
117
|
ctx,
|
|
121
118
|
deviceRunSessionId,
|
|
@@ -126,7 +123,7 @@ function createStartAppiumRemoteSessionBuildFunction(ctx) {
|
|
|
126
123
|
'appium:automationName': device.automationName,
|
|
127
124
|
'appium:udid': device.udid,
|
|
128
125
|
},
|
|
129
|
-
|
|
126
|
+
webPreviewUrl: webPreview.previewUrl,
|
|
130
127
|
},
|
|
131
128
|
logger,
|
|
132
129
|
});
|
|
@@ -144,8 +141,8 @@ function createStartAppiumRemoteSessionBuildFunction(ctx) {
|
|
|
144
141
|
});
|
|
145
142
|
}
|
|
146
143
|
finally {
|
|
147
|
-
if (
|
|
148
|
-
await
|
|
144
|
+
if (webPreview) {
|
|
145
|
+
await webPreview.stopAsync();
|
|
149
146
|
}
|
|
150
147
|
if (appiumTunnel) {
|
|
151
148
|
await appiumTunnel.stopAsync();
|
|
@@ -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
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
// the first moments
|
|
89
|
-
|
|
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)('
|
|
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)('
|
|
94
|
-
logger.info(`Launching ${ARGENT_PACKAGE_NAME}@${versionSpec} tool-server via
|
|
95
|
-
// Keep Argent itself in foreground mode under the detached
|
|
96
|
-
// the
|
|
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: '
|
|
104
|
+
command: 'bun',
|
|
99
105
|
args: [
|
|
106
|
+
'x',
|
|
100
107
|
`${ARGENT_PACKAGE_NAME}@${versionSpec}`,
|
|
101
108
|
'server',
|
|
102
109
|
'start',
|
|
@@ -146,7 +153,7 @@ function createStartArgentRemoteSessionBuildFunction(ctx) {
|
|
|
146
153
|
logger,
|
|
147
154
|
});
|
|
148
155
|
let toolsTunnel;
|
|
149
|
-
let
|
|
156
|
+
let webPreview;
|
|
150
157
|
try {
|
|
151
158
|
toolsTunnel = await (0, remoteDeviceRunSession_1.startNgrokTunnelAsync)({
|
|
152
159
|
port: toolServerPort,
|
|
@@ -158,25 +165,21 @@ function createStartArgentRemoteSessionBuildFunction(ctx) {
|
|
|
158
165
|
});
|
|
159
166
|
const publicToolsUrl = toolsTunnel.url;
|
|
160
167
|
logger.info(`Tunnel is ready at ${publicToolsUrl}.`);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
});
|
|
170
|
-
webPreviewUrl = serveSim.previewUrl;
|
|
171
|
-
logger.info(`Web preview URL: ${webPreviewUrl}`);
|
|
172
|
-
}
|
|
168
|
+
webPreview = await (0, remoteDeviceRunSession_1.startDeviceWebPreviewWithTunnelAsync)(ctx, {
|
|
169
|
+
runtimePlatform,
|
|
170
|
+
baseDomain: ngrokTunnelDomain,
|
|
171
|
+
env,
|
|
172
|
+
logger,
|
|
173
|
+
timeoutMs: STARTUP_TIMEOUT_MS,
|
|
174
|
+
});
|
|
175
|
+
logger.info(`Web preview URL: ${webPreview.previewUrl}`);
|
|
173
176
|
await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
|
|
174
177
|
ctx,
|
|
175
178
|
deviceRunSessionId,
|
|
176
179
|
remoteConfig: {
|
|
177
180
|
toolsUrl: publicToolsUrl,
|
|
178
181
|
...(toolServerToken ? { toolsAuthToken: toolServerToken } : {}),
|
|
179
|
-
|
|
182
|
+
webPreviewUrl: webPreview.previewUrl,
|
|
180
183
|
},
|
|
181
184
|
logger,
|
|
182
185
|
});
|
|
@@ -195,8 +198,8 @@ function createStartArgentRemoteSessionBuildFunction(ctx) {
|
|
|
195
198
|
});
|
|
196
199
|
}
|
|
197
200
|
finally {
|
|
198
|
-
if (
|
|
199
|
-
await
|
|
201
|
+
if (webPreview) {
|
|
202
|
+
await webPreview.stopAsync();
|
|
200
203
|
}
|
|
201
204
|
if (toolsTunnel) {
|
|
202
205
|
await toolsTunnel.stopAsync();
|
|
@@ -238,7 +241,7 @@ function warnIfArgentPackageVersionCannotBeVerified({ packageVersion, logger, })
|
|
|
238
241
|
if (!validVersion) {
|
|
239
242
|
logger.warn(`Argent remote simulator sessions require ${ARGENT_PACKAGE_NAME}@${exports.MIN_ARGENT_REMOTE_SESSION_VERSION} or newer, ` +
|
|
240
243
|
`but package_version "${packageVersion}" is not an exact semver version that EAS can verify. ` +
|
|
241
|
-
`Continuing and letting
|
|
244
|
+
`Continuing and letting bun x resolve it.`);
|
|
242
245
|
return;
|
|
243
246
|
}
|
|
244
247
|
if (semver_1.default.lt(validVersion, exports.MIN_ARGENT_REMOTE_SESSION_VERSION)) {
|
package/dist/steps/functions/{startServeSimRemoteSession.js → startWebPreviewRemoteSession.js}
RENAMED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createStartWebPreviewRemoteSessionBuildFunction = createStartWebPreviewRemoteSessionBuildFunction;
|
|
4
4
|
const steps_1 = require("@expo/steps");
|
|
5
5
|
const remoteDeviceRunSession_1 = require("../utils/remoteDeviceRunSession");
|
|
6
6
|
const STARTUP_TIMEOUT_MS = 60_000;
|
|
7
|
-
function
|
|
7
|
+
function createStartWebPreviewRemoteSessionBuildFunction(ctx) {
|
|
8
8
|
return new steps_1.BuildFunction({
|
|
9
9
|
namespace: 'eas',
|
|
10
10
|
id: 'start_serve_sim_remote_session',
|
|
11
|
-
name: 'Start
|
|
11
|
+
name: 'Start web preview remote session',
|
|
12
12
|
__metricsId: 'eas/start_serve_sim_remote_session',
|
|
13
|
-
supportedRuntimePlatforms: [steps_1.BuildRuntimePlatform.DARWIN],
|
|
14
13
|
inputProviders: [
|
|
15
14
|
steps_1.BuildStepInput.createProvider({
|
|
16
15
|
id: 'package_version',
|
|
@@ -23,26 +22,30 @@ function createStartServeSimRemoteSessionBuildFunction(ctx) {
|
|
|
23
22
|
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.NUMBER,
|
|
24
23
|
}),
|
|
25
24
|
],
|
|
26
|
-
fn: async ({ logger }, { inputs, env, signal }) => {
|
|
25
|
+
fn: async ({ logger, global }, { inputs, env, signal }) => {
|
|
27
26
|
const deviceRunSessionId = (0, remoteDeviceRunSession_1.getDeviceRunSessionIdOrThrow)(env);
|
|
28
27
|
const ngrokTunnelDomain = (0, remoteDeviceRunSession_1.getNgrokTunnelDomainOrThrow)(env);
|
|
29
28
|
const maxDurationSeconds = inputs.max_duration_seconds?.value;
|
|
30
29
|
const packageVersion = inputs.package_version?.value;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const { runtimePlatform } = global;
|
|
31
|
+
logger.info(`Starting web preview remote session (runtime: ${runtimePlatform}).`);
|
|
32
|
+
if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
|
|
33
|
+
await (0, remoteDeviceRunSession_1.selectXcodeDeveloperDirectoryAsync)({ env, logger });
|
|
34
|
+
}
|
|
35
|
+
const webPreview = await (0, remoteDeviceRunSession_1.startDeviceWebPreviewWithTunnelAsync)(ctx, {
|
|
36
|
+
runtimePlatform,
|
|
34
37
|
baseDomain: ngrokTunnelDomain,
|
|
35
38
|
env,
|
|
36
39
|
logger,
|
|
37
40
|
timeoutMs: STARTUP_TIMEOUT_MS,
|
|
38
41
|
packageVersion,
|
|
39
42
|
});
|
|
40
|
-
logger.info(`Preview URL: ${
|
|
43
|
+
logger.info(`Preview URL: ${webPreview.previewUrl}`);
|
|
41
44
|
try {
|
|
42
45
|
await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
|
|
43
46
|
ctx,
|
|
44
47
|
deviceRunSessionId,
|
|
45
|
-
remoteConfig: { previewUrl:
|
|
48
|
+
remoteConfig: { previewUrl: webPreview.previewUrl },
|
|
46
49
|
logger,
|
|
47
50
|
});
|
|
48
51
|
await (0, remoteDeviceRunSession_1.waitForDeviceRunSessionStoppedAsync)({
|
|
@@ -54,7 +57,7 @@ function createStartServeSimRemoteSessionBuildFunction(ctx) {
|
|
|
54
57
|
});
|
|
55
58
|
}
|
|
56
59
|
finally {
|
|
57
|
-
await
|
|
60
|
+
await webPreview.stopAsync();
|
|
58
61
|
}
|
|
59
62
|
},
|
|
60
63
|
});
|
|
@@ -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
|
|
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
|
-
|
|
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,43 +33,29 @@ 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;
|
|
55
40
|
}): Promise<void>;
|
|
56
41
|
/**
|
|
57
|
-
* Translate Cloudflare ICE servers into
|
|
42
|
+
* Translate Cloudflare ICE servers into web preview CLI flags: `--stun-url` (the
|
|
58
43
|
* credential-less entries) and `--turn-url`/`--turn-username`/`--turn-credential`
|
|
59
|
-
* (the entry carrying the short-lived credentials).
|
|
44
|
+
* (the entry carrying the short-lived credentials). serve-sim and expo-device-hub
|
|
45
|
+
* intentionally expose the same ICE flag contract.
|
|
60
46
|
*/
|
|
61
|
-
export declare function
|
|
47
|
+
export declare function turnIceServersToWebPreviewArgs(iceServers: TurnIceServers): string[];
|
|
62
48
|
/**
|
|
63
49
|
* Fetch short-lived Cloudflare TURN ICE servers for this job run from www
|
|
64
50
|
* (minted on demand, mirroring how the worker fetches project clone URLs) and
|
|
65
|
-
* translate them into
|
|
51
|
+
* translate them into web preview CLI flags.
|
|
66
52
|
*
|
|
67
|
-
* Best-effort: on any failure we log and return [] so
|
|
68
|
-
* its built-in P2P/STUN behavior. The credential is passed
|
|
69
|
-
*
|
|
70
|
-
*
|
|
53
|
+
* Best-effort: on any failure we log and return [] so the preview server falls
|
|
54
|
+
* back to its built-in P2P/STUN behavior. The credential is passed as a process
|
|
55
|
+
* arg and deliberately not logged (turtle-spawn never logs argv and the worker
|
|
56
|
+
* is single-tenant).
|
|
71
57
|
*/
|
|
72
|
-
export declare function
|
|
58
|
+
export declare function fetchWebPreviewTurnArgsAsync(ctx: CustomBuildContext, { env, logger }: {
|
|
73
59
|
env: BuildStepEnv;
|
|
74
60
|
logger: bunyan;
|
|
75
61
|
}): Promise<string[]>;
|
|
@@ -98,15 +84,22 @@ export declare function createServeSimArgs({ port, turnArgs, metricsCorsArgs, pa
|
|
|
98
84
|
metricsCorsArgs?: string[];
|
|
99
85
|
packageVersion?: string;
|
|
100
86
|
}): string[];
|
|
101
|
-
export declare function
|
|
102
|
-
|
|
87
|
+
export declare function createExpoDeviceHubArgs({ port, turnArgs, packageVersion, }: {
|
|
88
|
+
port: number;
|
|
89
|
+
turnArgs?: string[];
|
|
90
|
+
packageVersion?: string;
|
|
91
|
+
}): string[];
|
|
92
|
+
export declare function waitForWebPreviewReadyAsync({ previewServer, serverName, port, timeoutMs, }: {
|
|
93
|
+
previewServer: Pick<DetachedProcessHandle, 'pid' | 'getOutput'>;
|
|
94
|
+
serverName: string;
|
|
103
95
|
port: number;
|
|
104
96
|
timeoutMs: number;
|
|
105
97
|
}): Promise<void>;
|
|
106
|
-
export type
|
|
98
|
+
export type DeviceWebPreviewHandle = {
|
|
107
99
|
previewUrl: string;
|
|
108
100
|
stopAsync: () => Promise<void>;
|
|
109
101
|
};
|
|
102
|
+
export type ServeSimPreviewHandle = DeviceWebPreviewHandle;
|
|
110
103
|
export declare function startServeSimWithTunnelAsync(ctx: CustomBuildContext, { baseDomain, env, logger, timeoutMs, packageVersion, }: {
|
|
111
104
|
baseDomain: string;
|
|
112
105
|
env: BuildStepEnv;
|
|
@@ -114,6 +107,22 @@ export declare function startServeSimWithTunnelAsync(ctx: CustomBuildContext, {
|
|
|
114
107
|
timeoutMs: number;
|
|
115
108
|
packageVersion?: string;
|
|
116
109
|
}): Promise<ServeSimPreviewHandle>;
|
|
110
|
+
export declare function startExpoDeviceHubWithTunnelAsync(ctx: CustomBuildContext, { runtimePlatform, baseDomain, env, logger, timeoutMs, packageVersion, }: {
|
|
111
|
+
runtimePlatform: BuildRuntimePlatform;
|
|
112
|
+
baseDomain: string;
|
|
113
|
+
env: BuildStepEnv;
|
|
114
|
+
logger: bunyan;
|
|
115
|
+
timeoutMs: number;
|
|
116
|
+
packageVersion?: string;
|
|
117
|
+
}): Promise<DeviceWebPreviewHandle>;
|
|
118
|
+
export declare function startDeviceWebPreviewWithTunnelAsync(ctx: CustomBuildContext, { runtimePlatform, ...options }: {
|
|
119
|
+
runtimePlatform: BuildRuntimePlatform;
|
|
120
|
+
baseDomain: string;
|
|
121
|
+
env: BuildStepEnv;
|
|
122
|
+
logger: bunyan;
|
|
123
|
+
timeoutMs: number;
|
|
124
|
+
packageVersion?: string;
|
|
125
|
+
}): Promise<DeviceWebPreviewHandle>;
|
|
117
126
|
export type NgrokTunnelHandle = {
|
|
118
127
|
url: string;
|
|
119
128
|
stopAsync: () => Promise<void>;
|
|
@@ -41,15 +41,18 @@ exports.getNgrokTunnelDomainOrThrow = getNgrokTunnelDomainOrThrow;
|
|
|
41
41
|
exports.getNgrokAuthtokenOrThrow = getNgrokAuthtokenOrThrow;
|
|
42
42
|
exports.selectXcodeDeveloperDirectoryAsync = selectXcodeDeveloperDirectoryAsync;
|
|
43
43
|
exports.waitForDeviceRunSessionStoppedAsync = waitForDeviceRunSessionStoppedAsync;
|
|
44
|
-
exports.
|
|
45
|
-
exports.
|
|
46
|
-
exports.
|
|
44
|
+
exports.ensureFfmpegInstalledOnceAsync = ensureFfmpegInstalledOnceAsync;
|
|
45
|
+
exports.turnIceServersToWebPreviewArgs = turnIceServersToWebPreviewArgs;
|
|
46
|
+
exports.fetchWebPreviewTurnArgsAsync = fetchWebPreviewTurnArgsAsync;
|
|
47
47
|
exports.uploadRemoteSessionConfigAsync = uploadRemoteSessionConfigAsync;
|
|
48
48
|
exports.spawnDetached = spawnDetached;
|
|
49
49
|
exports.metricsCorsOriginToServeSimArgs = metricsCorsOriginToServeSimArgs;
|
|
50
50
|
exports.createServeSimArgs = createServeSimArgs;
|
|
51
|
-
exports.
|
|
51
|
+
exports.createExpoDeviceHubArgs = createExpoDeviceHubArgs;
|
|
52
|
+
exports.waitForWebPreviewReadyAsync = waitForWebPreviewReadyAsync;
|
|
52
53
|
exports.startServeSimWithTunnelAsync = startServeSimWithTunnelAsync;
|
|
54
|
+
exports.startExpoDeviceHubWithTunnelAsync = startExpoDeviceHubWithTunnelAsync;
|
|
55
|
+
exports.startDeviceWebPreviewWithTunnelAsync = startDeviceWebPreviewWithTunnelAsync;
|
|
53
56
|
exports.startNgrokTunnelAsync = startNgrokTunnelAsync;
|
|
54
57
|
exports.waitForFileAsync = waitForFileAsync;
|
|
55
58
|
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
@@ -69,12 +72,16 @@ const sentry_1 = require("../../sentry");
|
|
|
69
72
|
const retry_1 = require("../../utils/retry");
|
|
70
73
|
const turtleFetch_1 = require("../../utils/turtleFetch");
|
|
71
74
|
const XCODE_DEVELOPER_DIR = '/Applications/Xcode.app/Contents/Developer';
|
|
75
|
+
const WEB_PREVIEW_HOST = '127.0.0.1';
|
|
72
76
|
const SERVE_SIM_PACKAGE_NAME = '@expo/serve-sim';
|
|
73
|
-
const SERVE_SIM_HOST = '127.0.0.1';
|
|
74
77
|
const SERVE_SIM_MAX_DIMENSION = '960';
|
|
75
78
|
const SERVE_SIM_MJPEG_QUALITY = '0.55';
|
|
76
79
|
const SERVE_SIM_VIDEO_BITRATE = '6000000';
|
|
77
80
|
const SERVE_SIM_VIDEO_FPS = '60';
|
|
81
|
+
const EXPO_DEVICE_HUB_PACKAGE_NAME = 'expo-device-hub';
|
|
82
|
+
const EXPO_DEVICE_HUB_MAX_DIMENSION = '960';
|
|
83
|
+
const EXPO_DEVICE_HUB_VIDEO_BITRATE = '6000000';
|
|
84
|
+
const EXPO_DEVICE_HUB_VIDEO_FPS = '60';
|
|
78
85
|
const START_DEVICE_RUN_SESSION_MUTATION = (0, gql_tada_1.graphql)(`
|
|
79
86
|
mutation StartDeviceRunSession($deviceRunSessionId: ID!, $remoteConfig: JSONObject!) {
|
|
80
87
|
deviceRunSession {
|
|
@@ -262,10 +269,9 @@ async function sleepUntilAbortedAsync(timeoutMs, signal) {
|
|
|
262
269
|
}
|
|
263
270
|
}
|
|
264
271
|
}
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
// 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.
|
|
269
275
|
async function isFfmpegAvailableAsync(env) {
|
|
270
276
|
return (await (0, results_1.asyncResult)((0, turtle_spawn_1.default)('ffmpeg', ['-version'], { env }))).ok;
|
|
271
277
|
}
|
|
@@ -283,14 +289,14 @@ async function installFfmpegWithAptAsync({ env, logger, }) {
|
|
|
283
289
|
await (0, results_1.asyncResult)((0, turtle_spawn_1.default)('sudo', ['apt-get', 'update'], { env: aptEnv, logger }));
|
|
284
290
|
await (0, turtle_spawn_1.default)('sudo', ['apt-get', 'install', '-y', 'ffmpeg'], { env: aptEnv, logger });
|
|
285
291
|
}
|
|
292
|
+
let ffmpegSetupPromise;
|
|
286
293
|
/**
|
|
287
|
-
* Install ffmpeg when the runtime does not already provide it
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
* 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.
|
|
291
297
|
*
|
|
292
|
-
* Best-effort by design:
|
|
293
|
-
*
|
|
298
|
+
* Best-effort by design: a failure here is logged and the session continues
|
|
299
|
+
* without FFmpeg-dependent features.
|
|
294
300
|
*
|
|
295
301
|
* The whole body is wrapped because the caller runs this in the background with
|
|
296
302
|
* `void`. There is no unhandledRejection handler in the worker, so a rejection
|
|
@@ -305,7 +311,7 @@ async function ensureFfmpegInstalledAsync({ runtimePlatform, env, logger, }) {
|
|
|
305
311
|
return;
|
|
306
312
|
}
|
|
307
313
|
const isDarwin = runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN;
|
|
308
|
-
logger.info(`ffmpeg is not installed, installing it with ${isDarwin ? 'Homebrew' : 'apt'} for
|
|
314
|
+
logger.info(`ffmpeg is not installed, installing it with ${isDarwin ? 'Homebrew' : 'apt'} for the device session.`);
|
|
309
315
|
if (isDarwin) {
|
|
310
316
|
await installFfmpegWithHomebrewAsync({ env, logger });
|
|
311
317
|
}
|
|
@@ -316,10 +322,26 @@ async function ensureFfmpegInstalledAsync({ runtimePlatform, env, logger, }) {
|
|
|
316
322
|
}
|
|
317
323
|
catch (err) {
|
|
318
324
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
319
|
-
sentry_1.Sentry.capture('Could not install ffmpeg for
|
|
325
|
+
sentry_1.Sentry.capture('Could not install ffmpeg for the device session', error, {
|
|
320
326
|
level: 'warning',
|
|
321
327
|
});
|
|
322
|
-
logger.warn({ err: error }, 'Could not install ffmpeg.
|
|
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
|
+
}
|
|
323
345
|
}
|
|
324
346
|
}
|
|
325
347
|
const TurnIceServersResponseSchema = zod_1.z.object({
|
|
@@ -328,11 +350,12 @@ const TurnIceServersResponseSchema = zod_1.z.object({
|
|
|
328
350
|
}),
|
|
329
351
|
});
|
|
330
352
|
/**
|
|
331
|
-
* Translate Cloudflare ICE servers into
|
|
353
|
+
* Translate Cloudflare ICE servers into web preview CLI flags: `--stun-url` (the
|
|
332
354
|
* credential-less entries) and `--turn-url`/`--turn-username`/`--turn-credential`
|
|
333
|
-
* (the entry carrying the short-lived credentials).
|
|
355
|
+
* (the entry carrying the short-lived credentials). serve-sim and expo-device-hub
|
|
356
|
+
* intentionally expose the same ICE flag contract.
|
|
334
357
|
*/
|
|
335
|
-
function
|
|
358
|
+
function turnIceServersToWebPreviewArgs(iceServers) {
|
|
336
359
|
const stunUrls = iceServers
|
|
337
360
|
.filter(server => !server.username && !server.credential)
|
|
338
361
|
.flatMap(server => server.urls);
|
|
@@ -349,14 +372,14 @@ function turnIceServersToServeSimArgs(iceServers) {
|
|
|
349
372
|
/**
|
|
350
373
|
* Fetch short-lived Cloudflare TURN ICE servers for this job run from www
|
|
351
374
|
* (minted on demand, mirroring how the worker fetches project clone URLs) and
|
|
352
|
-
* translate them into
|
|
375
|
+
* translate them into web preview CLI flags.
|
|
353
376
|
*
|
|
354
|
-
* Best-effort: on any failure we log and return [] so
|
|
355
|
-
* its built-in P2P/STUN behavior. The credential is passed
|
|
356
|
-
*
|
|
357
|
-
*
|
|
377
|
+
* Best-effort: on any failure we log and return [] so the preview server falls
|
|
378
|
+
* back to its built-in P2P/STUN behavior. The credential is passed as a process
|
|
379
|
+
* arg and deliberately not logged (turtle-spawn never logs argv and the worker
|
|
380
|
+
* is single-tenant).
|
|
358
381
|
*/
|
|
359
|
-
async function
|
|
382
|
+
async function fetchWebPreviewTurnArgsAsync(ctx, { env, logger }) {
|
|
360
383
|
try {
|
|
361
384
|
const deviceRunSessionId = getDeviceRunSessionIdOrThrow(env);
|
|
362
385
|
const expoApiServerUrl = (0, nullthrows_1.default)(ctx.env.__API_SERVER_URL, '__API_SERVER_URL is not set');
|
|
@@ -370,16 +393,16 @@ async function fetchServeSimTurnArgsAsync(ctx, { env, logger }) {
|
|
|
370
393
|
logger,
|
|
371
394
|
});
|
|
372
395
|
const { data } = TurnIceServersResponseSchema.parse(await response.json());
|
|
373
|
-
const args =
|
|
396
|
+
const args = turnIceServersToWebPreviewArgs(data.iceServers);
|
|
374
397
|
if (args.length > 0) {
|
|
375
|
-
logger.info('Configured
|
|
398
|
+
logger.info('Configured the web preview with Cloudflare TURN ICE servers.');
|
|
376
399
|
}
|
|
377
400
|
return args;
|
|
378
401
|
}
|
|
379
402
|
catch (err) {
|
|
380
403
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
381
404
|
sentry_1.Sentry.capture('Could not fetch Cloudflare TURN ICE servers', error, { level: 'warning' });
|
|
382
|
-
logger.warn({ err: error }, 'Could not fetch Cloudflare TURN ICE servers;
|
|
405
|
+
logger.warn({ err: error }, 'Could not fetch Cloudflare TURN ICE servers; the web preview will fall back to P2P/STUN.');
|
|
383
406
|
return [];
|
|
384
407
|
}
|
|
385
408
|
}
|
|
@@ -476,6 +499,9 @@ function metricsCorsOriginToServeSimArgs(env) {
|
|
|
476
499
|
function createServeSimPackageSpec(packageVersion) {
|
|
477
500
|
return `${SERVE_SIM_PACKAGE_NAME}@${packageVersion ?? 'latest'}`;
|
|
478
501
|
}
|
|
502
|
+
function createExpoDeviceHubPackageSpec(packageVersion) {
|
|
503
|
+
return `${EXPO_DEVICE_HUB_PACKAGE_NAME}@${packageVersion ?? 'latest'}`;
|
|
504
|
+
}
|
|
479
505
|
function createServeSimArgs({ port, turnArgs = [], metricsCorsArgs = [], packageVersion, }) {
|
|
480
506
|
return [
|
|
481
507
|
'--yes',
|
|
@@ -483,7 +509,7 @@ function createServeSimArgs({ port, turnArgs = [], metricsCorsArgs = [], package
|
|
|
483
509
|
'--port',
|
|
484
510
|
String(port),
|
|
485
511
|
'--host',
|
|
486
|
-
|
|
512
|
+
WEB_PREVIEW_HOST,
|
|
487
513
|
'--transport',
|
|
488
514
|
'webrtc',
|
|
489
515
|
'--webrtc-codec',
|
|
@@ -500,40 +526,67 @@ function createServeSimArgs({ port, turnArgs = [], metricsCorsArgs = [], package
|
|
|
500
526
|
...metricsCorsArgs,
|
|
501
527
|
];
|
|
502
528
|
}
|
|
529
|
+
function createExpoDeviceHubArgs({ port, turnArgs = [], packageVersion, }) {
|
|
530
|
+
return [
|
|
531
|
+
'--yes',
|
|
532
|
+
createExpoDeviceHubPackageSpec(packageVersion),
|
|
533
|
+
'--port',
|
|
534
|
+
String(port),
|
|
535
|
+
'--host',
|
|
536
|
+
WEB_PREVIEW_HOST,
|
|
537
|
+
'--platform',
|
|
538
|
+
'android',
|
|
539
|
+
'--transport',
|
|
540
|
+
'webrtc',
|
|
541
|
+
'--webrtc-codec',
|
|
542
|
+
'h264',
|
|
543
|
+
'--webrtc-ice-policy',
|
|
544
|
+
'all',
|
|
545
|
+
'--max-dimension',
|
|
546
|
+
EXPO_DEVICE_HUB_MAX_DIMENSION,
|
|
547
|
+
'--video-bitrate',
|
|
548
|
+
EXPO_DEVICE_HUB_VIDEO_BITRATE,
|
|
549
|
+
'--video-fps',
|
|
550
|
+
EXPO_DEVICE_HUB_VIDEO_FPS,
|
|
551
|
+
'--hide-sidebar',
|
|
552
|
+
'--hide-boot-device',
|
|
553
|
+
...turnArgs,
|
|
554
|
+
];
|
|
555
|
+
}
|
|
503
556
|
async function findAvailablePortAsync() {
|
|
504
557
|
const server = (0, node_net_1.createServer)();
|
|
505
558
|
server.unref();
|
|
506
559
|
await new Promise((resolve, reject) => {
|
|
507
560
|
server.once('error', reject);
|
|
508
|
-
server.listen(0,
|
|
561
|
+
server.listen(0, WEB_PREVIEW_HOST, () => resolve());
|
|
509
562
|
});
|
|
510
563
|
const address = server.address();
|
|
511
564
|
await new Promise((resolve, reject) => {
|
|
512
565
|
server.close(err => (err ? reject(err) : resolve()));
|
|
513
566
|
});
|
|
514
567
|
if (!address || typeof address === 'string') {
|
|
515
|
-
throw new eas_build_job_1.SystemError('Could not allocate a local port for
|
|
568
|
+
throw new eas_build_job_1.SystemError('Could not allocate a local port for the web preview.');
|
|
516
569
|
}
|
|
517
570
|
return address.port;
|
|
518
571
|
}
|
|
519
|
-
const
|
|
572
|
+
const WebPreviewReadyResponseSchema = zod_1.z.object({
|
|
520
573
|
status: zod_1.z.literal('ready'),
|
|
521
574
|
device: zod_1.z.string(),
|
|
522
575
|
});
|
|
523
|
-
async function
|
|
524
|
-
const readyUrl = `http://${
|
|
576
|
+
async function waitForWebPreviewReadyAsync({ previewServer, serverName, port, timeoutMs, }) {
|
|
577
|
+
const readyUrl = `http://${WEB_PREVIEW_HOST}:${port}/readyz`;
|
|
525
578
|
const deadline = Date.now() + timeoutMs;
|
|
526
579
|
let lastError;
|
|
527
580
|
while (Date.now() < deadline) {
|
|
528
|
-
if (
|
|
529
|
-
throw new eas_build_job_1.SystemError(
|
|
581
|
+
if (previewServer.pid !== undefined && !isProcessRunning(previewServer.pid)) {
|
|
582
|
+
throw new eas_build_job_1.SystemError(`${serverName} exited before becoming ready. Last output:\n${previewServer.getOutput() || '<empty>'}`);
|
|
530
583
|
}
|
|
531
584
|
try {
|
|
532
585
|
const response = await (0, turtleFetch_1.turtleFetch)(readyUrl, 'GET', {
|
|
533
586
|
retries: 0,
|
|
534
587
|
timeout: 2_000,
|
|
535
588
|
});
|
|
536
|
-
|
|
589
|
+
WebPreviewReadyResponseSchema.parse(await response.json());
|
|
537
590
|
return;
|
|
538
591
|
}
|
|
539
592
|
catch (error) {
|
|
@@ -541,21 +594,20 @@ async function waitForServeSimReadyAsync({ serveSim, port, timeoutMs, }) {
|
|
|
541
594
|
}
|
|
542
595
|
await (0, retry_1.sleepAsync)(1_000);
|
|
543
596
|
}
|
|
544
|
-
throw new eas_build_job_1.SystemError(`Timed out waiting for
|
|
597
|
+
throw new eas_build_job_1.SystemError(`Timed out waiting for ${serverName} readiness at ${readyUrl}${lastError instanceof Error ? `: ${lastError.message}` : ''}. Last output:\n${previewServer.getOutput() || '<empty>'}`);
|
|
545
598
|
}
|
|
546
|
-
async function
|
|
599
|
+
async function startWebPreviewWithTunnelAsync(ctx, { baseDomain, env, logger, timeoutMs, serverName, packageSpec, createArgs, }) {
|
|
547
600
|
const port = await findAvailablePortAsync();
|
|
548
|
-
logger.info(`Launching ${
|
|
549
|
-
const turnArgs = await
|
|
550
|
-
const
|
|
551
|
-
const serveSim = spawnDetached({
|
|
601
|
+
logger.info(`Launching ${packageSpec} on ${WEB_PREVIEW_HOST}:${port}.`);
|
|
602
|
+
const turnArgs = await fetchWebPreviewTurnArgsAsync(ctx, { env, logger });
|
|
603
|
+
const previewServer = spawnDetached({
|
|
552
604
|
command: 'npx',
|
|
553
|
-
args:
|
|
605
|
+
args: createArgs(port, turnArgs),
|
|
554
606
|
env,
|
|
555
607
|
});
|
|
556
608
|
try {
|
|
557
|
-
logger.info(
|
|
558
|
-
await
|
|
609
|
+
logger.info(`Waiting for ${serverName} to become ready.`);
|
|
610
|
+
await waitForWebPreviewReadyAsync({ previewServer, serverName, port, timeoutMs });
|
|
559
611
|
const tunnel = await startNgrokTunnelAsync({
|
|
560
612
|
port,
|
|
561
613
|
subdomainPrefix: 'web-preview',
|
|
@@ -566,20 +618,54 @@ async function startServeSimWithTunnelAsync(ctx, { baseDomain, env, logger, time
|
|
|
566
618
|
return {
|
|
567
619
|
previewUrl: tunnel.url,
|
|
568
620
|
stopAsync: async () => {
|
|
569
|
-
const results = await Promise.allSettled([tunnel.stopAsync(),
|
|
621
|
+
const results = await Promise.allSettled([tunnel.stopAsync(), previewServer.stopAsync()]);
|
|
570
622
|
for (const result of results) {
|
|
571
623
|
if (result.status === 'rejected') {
|
|
572
|
-
logger.warn({ err: result.reason },
|
|
624
|
+
logger.warn({ err: result.reason }, `Could not stop a ${serverName} preview resource.`);
|
|
573
625
|
}
|
|
574
626
|
}
|
|
575
627
|
},
|
|
576
628
|
};
|
|
577
629
|
}
|
|
578
630
|
catch (error) {
|
|
579
|
-
await
|
|
631
|
+
await previewServer.stopAsync();
|
|
580
632
|
throw error;
|
|
581
633
|
}
|
|
582
634
|
}
|
|
635
|
+
async function startServeSimWithTunnelAsync(ctx, { baseDomain, env, logger, timeoutMs, packageVersion, }) {
|
|
636
|
+
const metricsCorsArgs = metricsCorsOriginToServeSimArgs(env);
|
|
637
|
+
return await startWebPreviewWithTunnelAsync(ctx, {
|
|
638
|
+
baseDomain,
|
|
639
|
+
env,
|
|
640
|
+
logger,
|
|
641
|
+
timeoutMs,
|
|
642
|
+
serverName: 'serve-sim',
|
|
643
|
+
packageSpec: createServeSimPackageSpec(packageVersion),
|
|
644
|
+
createArgs: (port, turnArgs) => createServeSimArgs({ port, turnArgs, metricsCorsArgs, packageVersion }),
|
|
645
|
+
});
|
|
646
|
+
}
|
|
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
|
+
}
|
|
651
|
+
return await startWebPreviewWithTunnelAsync(ctx, {
|
|
652
|
+
baseDomain,
|
|
653
|
+
env,
|
|
654
|
+
logger,
|
|
655
|
+
timeoutMs,
|
|
656
|
+
serverName: 'expo-device-hub',
|
|
657
|
+
packageSpec: createExpoDeviceHubPackageSpec(packageVersion),
|
|
658
|
+
createArgs: (port, turnArgs) => createExpoDeviceHubArgs({ port, turnArgs, packageVersion }),
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
async function startDeviceWebPreviewWithTunnelAsync(ctx, { runtimePlatform, ...options }) {
|
|
662
|
+
switch (runtimePlatform) {
|
|
663
|
+
case steps_1.BuildRuntimePlatform.DARWIN:
|
|
664
|
+
return await startServeSimWithTunnelAsync(ctx, options);
|
|
665
|
+
case steps_1.BuildRuntimePlatform.LINUX:
|
|
666
|
+
return await startExpoDeviceHubWithTunnelAsync(ctx, { ...options, runtimePlatform });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
583
669
|
async function startNgrokTunnelAsync({ port, subdomainPrefix, baseDomain, authtoken, rewriteHostHeader, logger, }) {
|
|
584
670
|
const domain = `${subdomainPrefix}-${(0, node_crypto_1.randomBytes)(16).toString('hex')}.${baseDomain}`;
|
|
585
671
|
logger.info(`Starting ngrok tunnel ${domain} -> http://localhost:${port}.`);
|
|
@@ -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,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": "
|
|
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": "
|
|
43
|
-
"@expo/eas-build-job": "
|
|
42
|
+
"@expo/downloader": "24.0.0",
|
|
43
|
+
"@expo/eas-build-job": "24.0.0",
|
|
44
44
|
"@expo/env": "^0.4.0",
|
|
45
|
-
"@expo/logger": "
|
|
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": "
|
|
51
|
-
"@expo/template-file": "
|
|
52
|
-
"@expo/turtle-spawn": "
|
|
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": "
|
|
105
|
+
"gitHead": "5409c03d58c50b80e4ec9d5ca40f2fbdfaba9aa0"
|
|
106
106
|
}
|