@expo/build-tools 1.0.197 → 1.0.199
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/steps/functions/internalMaestroTest.d.ts +2 -3
- package/dist/steps/functions/internalMaestroTest.js +160 -72
- package/dist/steps/functions/internalMaestroTest.js.map +1 -1
- package/dist/utils/AndroidEmulatorUtils.d.ts +6 -0
- package/dist/utils/AndroidEmulatorUtils.js +30 -0
- package/dist/utils/AndroidEmulatorUtils.js.map +1 -1
- package/dist/utils/IosSimulatorUtils.d.ts +10 -4
- package/dist/utils/IosSimulatorUtils.js +15 -6
- package/dist/utils/IosSimulatorUtils.js.map +1 -1
- package/dist/utils/findMaestroPathsFlowsToExecuteAsync.d.ts +9 -0
- package/dist/utils/findMaestroPathsFlowsToExecuteAsync.js +127 -0
- package/dist/utils/findMaestroPathsFlowsToExecuteAsync.js.map +1 -0
- package/dist/utils/strings.d.ts +2 -0
- package/dist/utils/strings.js +6 -1
- package/dist/utils/strings.js.map +1 -1
- package/package.json +4 -3
|
@@ -3,8 +3,7 @@ import { CustomBuildContext } from '../../customBuildContext';
|
|
|
3
3
|
export declare function createInternalEasMaestroTestFunction(ctx: CustomBuildContext): BuildFunction;
|
|
4
4
|
export declare function getMaestroTestCommand(params: {
|
|
5
5
|
flow_path: string;
|
|
6
|
-
include_tags: string | undefined;
|
|
7
|
-
exclude_tags: string | undefined;
|
|
8
6
|
output_format: string | undefined;
|
|
9
|
-
|
|
7
|
+
/** Unused if `output_format` is undefined */
|
|
8
|
+
output_path: string;
|
|
10
9
|
}): [command: string, ...args: string[]];
|
|
@@ -4,8 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getMaestroTestCommand = exports.createInternalEasMaestroTestFunction = void 0;
|
|
7
|
-
const
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
9
10
|
const steps_1 = require("@expo/steps");
|
|
10
11
|
const zod_1 = require("zod");
|
|
11
12
|
const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
|
|
@@ -14,6 +15,8 @@ const results_1 = require("@expo/results");
|
|
|
14
15
|
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
15
16
|
const IosSimulatorUtils_1 = require("../../utils/IosSimulatorUtils");
|
|
16
17
|
const AndroidEmulatorUtils_1 = require("../../utils/AndroidEmulatorUtils");
|
|
18
|
+
const strings_1 = require("../../utils/strings");
|
|
19
|
+
const findMaestroPathsFlowsToExecuteAsync_1 = require("../../utils/findMaestroPathsFlowsToExecuteAsync");
|
|
17
20
|
function createInternalEasMaestroTestFunction(ctx) {
|
|
18
21
|
return new steps_1.BuildFunction({
|
|
19
22
|
namespace: 'eas',
|
|
@@ -64,7 +67,7 @@ function createInternalEasMaestroTestFunction(ctx) {
|
|
|
64
67
|
}),
|
|
65
68
|
],
|
|
66
69
|
fn: async (stepCtx, { inputs: _inputs, env }) => {
|
|
67
|
-
|
|
70
|
+
var _a;
|
|
68
71
|
// inputs come in form of { value: unknown }. Here we parse them into a typed and validated object.
|
|
69
72
|
const { platform, flow_paths, retries, include_tags, exclude_tags, shards, output_format, record_screen, } = zod_1.z
|
|
70
73
|
.object({
|
|
@@ -78,10 +81,29 @@ function createInternalEasMaestroTestFunction(ctx) {
|
|
|
78
81
|
record_screen: zod_1.z.boolean().default(false),
|
|
79
82
|
})
|
|
80
83
|
.parse(Object.fromEntries(Object.entries(_inputs).map(([key, value]) => [key, value.value])));
|
|
84
|
+
const flowPathsToExecute = [];
|
|
85
|
+
for (const flowPath of flow_paths) {
|
|
86
|
+
const flowPaths = await (0, findMaestroPathsFlowsToExecuteAsync_1.findMaestroPathsFlowsToExecuteAsync)({
|
|
87
|
+
workingDirectory: stepCtx.workingDirectory,
|
|
88
|
+
flowPath,
|
|
89
|
+
logger: stepCtx.logger,
|
|
90
|
+
includeTags: include_tags ? include_tags.split(',') : undefined,
|
|
91
|
+
excludeTags: exclude_tags ? exclude_tags.split(',') : undefined,
|
|
92
|
+
});
|
|
93
|
+
if (flowPaths.length === 0) {
|
|
94
|
+
stepCtx.logger.warn(`No flows to execute found in "${flowPath}".`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
stepCtx.logger.info(`Marking for execution:\n- ${flowPaths.map((flowPath) => node_path_1.default.relative(stepCtx.workingDirectory, flowPath)).join('\n- ')}`);
|
|
98
|
+
stepCtx.logger.info('');
|
|
99
|
+
flowPathsToExecute.push(...flowPaths);
|
|
100
|
+
}
|
|
81
101
|
// TODO: Add support for shards. (Shouldn't be too difficult.)
|
|
82
102
|
if (shards > 1) {
|
|
83
103
|
stepCtx.logger.warn('Sharding support has been temporarily disabled. Running tests on a single shard.');
|
|
84
104
|
}
|
|
105
|
+
// eas/__maestro_test does not start devices, it expects a single device to be already running
|
|
106
|
+
// and configured with the app. Here we find the booted device and stop it.
|
|
85
107
|
let sourceDeviceIdentifier;
|
|
86
108
|
switch (platform) {
|
|
87
109
|
case 'ios': {
|
|
@@ -102,7 +124,7 @@ function createInternalEasMaestroTestFunction(ctx) {
|
|
|
102
124
|
logger: stepCtx.logger,
|
|
103
125
|
stdio: 'pipe',
|
|
104
126
|
});
|
|
105
|
-
sourceDeviceIdentifier = device.
|
|
127
|
+
sourceDeviceIdentifier = device.name;
|
|
106
128
|
break;
|
|
107
129
|
}
|
|
108
130
|
case 'android': {
|
|
@@ -124,19 +146,32 @@ function createInternalEasMaestroTestFunction(ctx) {
|
|
|
124
146
|
stepCtx.logger.info(`Running tests on Android Emulator: ${avdName}.`);
|
|
125
147
|
stepCtx.logger.info(`Preparing Emulator for tests...`);
|
|
126
148
|
await (0, steps_1.spawnAsync)('adb', ['-s', serialId, 'emu', 'kill'], {
|
|
127
|
-
logger: stepCtx.logger,
|
|
128
149
|
stdio: 'pipe',
|
|
129
150
|
});
|
|
130
151
|
sourceDeviceIdentifier = avdName;
|
|
131
152
|
break;
|
|
132
153
|
}
|
|
133
154
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
155
|
+
// During tests we generate reports and device logs. We store them in temporary directories
|
|
156
|
+
// and upload them once all tests are done. When a test is retried, new reports overwrite
|
|
157
|
+
// the old ones. The files are named "flow-${index}" for easier identification.
|
|
158
|
+
const maestroReportsDir = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'maestro-reports-'));
|
|
159
|
+
const deviceLogsDir = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'device-logs-'));
|
|
160
|
+
const failedFlows = [];
|
|
161
|
+
for (const [flowIndex, flowPath] of flowPathsToExecute.entries()) {
|
|
162
|
+
stepCtx.logger.info('');
|
|
163
|
+
// If output_format is empty or noop, we won't use this.
|
|
164
|
+
const outputPath = node_path_1.default.join(maestroReportsDir, [
|
|
165
|
+
`${(_a = output_format + '-') !== null && _a !== void 0 ? _a : ''}report-flow-${flowIndex + 1}`,
|
|
166
|
+
MaestroOutputFormatToExtensionMap[output_format !== null && output_format !== void 0 ? output_format : 'noop'],
|
|
167
|
+
]
|
|
168
|
+
.filter(Boolean)
|
|
169
|
+
.join('.'));
|
|
170
|
+
for (let attemptCount = 0; attemptCount < retries; attemptCount++) {
|
|
171
|
+
const localDeviceName = `eas-simulator-${flowIndex}-${attemptCount}`;
|
|
137
172
|
// If the test passes, but the recording fails, we don't want to make the test fail,
|
|
138
173
|
// so we return two separate results.
|
|
139
|
-
const { fnResult, recordingResult } = await withCleanDeviceAsync({
|
|
174
|
+
const { fnResult: { fnResult, recordingResult }, logsResult, } = await withCleanDeviceAsync({
|
|
140
175
|
platform,
|
|
141
176
|
sourceDeviceIdentifier,
|
|
142
177
|
localDeviceName,
|
|
@@ -150,40 +185,60 @@ function createInternalEasMaestroTestFunction(ctx) {
|
|
|
150
185
|
env,
|
|
151
186
|
logger: stepCtx.logger,
|
|
152
187
|
fn: async () => {
|
|
153
|
-
|
|
188
|
+
stepCtx.logger.info('');
|
|
154
189
|
const [command, ...args] = getMaestroTestCommand({
|
|
155
190
|
flow_path: flowPath,
|
|
156
|
-
include_tags,
|
|
157
|
-
exclude_tags,
|
|
158
191
|
output_format,
|
|
159
|
-
output_path:
|
|
160
|
-
outputFormat: output_format !== null && output_format !== void 0 ? output_format : 'noop',
|
|
161
|
-
env,
|
|
162
|
-
})) !== null && _a !== void 0 ? _a : undefined,
|
|
163
|
-
});
|
|
164
|
-
await (0, steps_1.spawnAsync)(command, args, {
|
|
165
|
-
logger: stepCtx.logger,
|
|
166
|
-
cwd: stepCtx.workingDirectory,
|
|
167
|
-
env,
|
|
168
|
-
stdio: 'pipe',
|
|
192
|
+
output_path: outputPath,
|
|
169
193
|
});
|
|
194
|
+
try {
|
|
195
|
+
await (0, steps_1.spawnAsync)(command, args, {
|
|
196
|
+
logger: stepCtx.logger,
|
|
197
|
+
cwd: stepCtx.workingDirectory,
|
|
198
|
+
env,
|
|
199
|
+
stdio: 'pipe',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
stepCtx.logger.info('');
|
|
204
|
+
}
|
|
170
205
|
},
|
|
171
206
|
});
|
|
172
207
|
},
|
|
173
208
|
});
|
|
174
|
-
|
|
209
|
+
// Move device logs to the device logs directory.
|
|
210
|
+
if (logsResult === null || logsResult === void 0 ? void 0 : logsResult.ok) {
|
|
211
|
+
try {
|
|
212
|
+
const extension = node_path_1.default.extname(logsResult.value.outputPath);
|
|
213
|
+
const destinationPath = node_path_1.default.join(deviceLogsDir, `flow-${flowIndex}${extension}`);
|
|
214
|
+
await node_fs_1.default.promises.rm(destinationPath, {
|
|
215
|
+
force: true,
|
|
216
|
+
recursive: true,
|
|
217
|
+
});
|
|
218
|
+
await node_fs_1.default.promises.rename(logsResult.value.outputPath, destinationPath);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
stepCtx.logger.warn({ err }, 'Failed to prepare device logs for upload.');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else if (logsResult === null || logsResult === void 0 ? void 0 : logsResult.reason) {
|
|
225
|
+
stepCtx.logger.error({ err: logsResult.reason }, 'Failed to collect device logs.');
|
|
226
|
+
}
|
|
227
|
+
const isLastAttempt = fnResult.ok || attemptCount === retries - 1;
|
|
228
|
+
if (isLastAttempt && recordingResult.value) {
|
|
175
229
|
try {
|
|
176
230
|
await ctx.runtimeApi.uploadArtifact({
|
|
177
231
|
logger: stepCtx.logger,
|
|
178
232
|
artifact: {
|
|
179
|
-
|
|
233
|
+
// TODO(sjchmiela): Add metadata to artifacts so we don't need to encode flow path and attempt in the name.
|
|
234
|
+
name: `Screen Recording (${flowPath})`,
|
|
180
235
|
paths: [recordingResult.value],
|
|
181
236
|
type: eas_build_job_1.GenericArtifactType.OTHER,
|
|
182
237
|
},
|
|
183
238
|
});
|
|
184
239
|
}
|
|
185
240
|
catch (err) {
|
|
186
|
-
stepCtx.logger.warn('Failed to upload screen recording.'
|
|
241
|
+
stepCtx.logger.warn({ err }, 'Failed to upload screen recording.');
|
|
187
242
|
}
|
|
188
243
|
}
|
|
189
244
|
if (fnResult.ok) {
|
|
@@ -191,62 +246,84 @@ function createInternalEasMaestroTestFunction(ctx) {
|
|
|
191
246
|
// Break out of the retry loop.
|
|
192
247
|
break;
|
|
193
248
|
}
|
|
194
|
-
|
|
195
|
-
|
|
249
|
+
if (attemptCount < retries - 1) {
|
|
250
|
+
stepCtx.logger.info(`Retrying test...`);
|
|
251
|
+
stepCtx.logger.info('');
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
// fnResult.reason is not super interesting, but it does print out the full command so we can keep it for debugging purposes.
|
|
255
|
+
stepCtx.logger.error({ err: fnResult.reason }, 'Test errored.');
|
|
256
|
+
failedFlows.push(flowPath);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
stepCtx.logger.info('');
|
|
260
|
+
// When all tests are done, we upload the reports and device logs.
|
|
261
|
+
const generatedMaestroReports = await node_fs_1.default.promises.readdir(maestroReportsDir);
|
|
262
|
+
if (generatedMaestroReports.length === 0) {
|
|
263
|
+
stepCtx.logger.warn('No reports were generated.');
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
stepCtx.logger.info(`Uploading reports...`);
|
|
267
|
+
try {
|
|
268
|
+
await ctx.runtimeApi.uploadArtifact({
|
|
269
|
+
logger: stepCtx.logger,
|
|
270
|
+
artifact: {
|
|
271
|
+
name: `${strings_1.PlatformToProperNounMap[platform]} Maestro Test Reports (${output_format})`,
|
|
272
|
+
paths: [maestroReportsDir],
|
|
273
|
+
type: eas_build_job_1.GenericArtifactType.OTHER,
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
stepCtx.logger.error({ err }, 'Failed to upload reports.');
|
|
196
279
|
}
|
|
197
280
|
}
|
|
281
|
+
const generatedDeviceLogs = await node_fs_1.default.promises.readdir(deviceLogsDir);
|
|
282
|
+
if (generatedDeviceLogs.length === 0) {
|
|
283
|
+
stepCtx.logger.warn('No device logs were successfully collected.');
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
stepCtx.logger.info(`Uploading device logs...`);
|
|
287
|
+
try {
|
|
288
|
+
await ctx.runtimeApi.uploadArtifact({
|
|
289
|
+
logger: stepCtx.logger,
|
|
290
|
+
artifact: {
|
|
291
|
+
name: `Maestro Test Device Logs`,
|
|
292
|
+
paths: [deviceLogsDir],
|
|
293
|
+
type: eas_build_job_1.GenericArtifactType.OTHER,
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
stepCtx.logger.error({ err }, 'Failed to upload device logs.');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
stepCtx.logger.info('');
|
|
302
|
+
// If any tests failed, we throw an error to mark the step as failed.
|
|
303
|
+
if (failedFlows.length > 0) {
|
|
304
|
+
throw new Error(`Some Maestro tests failed:\n- ${failedFlows
|
|
305
|
+
.map((flowPath) => node_path_1.default.relative(stepCtx.workingDirectory, flowPath))
|
|
306
|
+
.join('\n- ')}`);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
stepCtx.logger.info('All Maestro tests passed.');
|
|
310
|
+
}
|
|
198
311
|
},
|
|
199
312
|
});
|
|
200
313
|
}
|
|
201
314
|
exports.createInternalEasMaestroTestFunction = createInternalEasMaestroTestFunction;
|
|
202
315
|
function getMaestroTestCommand(params) {
|
|
203
|
-
let includeTagsFlag = '';
|
|
204
|
-
if (typeof params.include_tags === 'string') {
|
|
205
|
-
includeTagsFlag = `--include-tags=${params.include_tags}`;
|
|
206
|
-
}
|
|
207
|
-
let excludeTagsFlag = '';
|
|
208
|
-
if (typeof params.exclude_tags === 'string') {
|
|
209
|
-
excludeTagsFlag = `--exclude-tags=${params.exclude_tags}`;
|
|
210
|
-
}
|
|
211
316
|
let outputFormatFlags = [];
|
|
212
317
|
if (params.output_format) {
|
|
213
|
-
outputFormatFlags = [`--format
|
|
318
|
+
outputFormatFlags = [`--format`, params.output_format, `--output`, params.output_path];
|
|
214
319
|
}
|
|
215
|
-
return [
|
|
216
|
-
'maestro',
|
|
217
|
-
'test',
|
|
218
|
-
includeTagsFlag,
|
|
219
|
-
excludeTagsFlag,
|
|
220
|
-
...outputFormatFlags,
|
|
221
|
-
params.flow_path,
|
|
222
|
-
].flatMap((e) => e || []);
|
|
320
|
+
return ['maestro', 'test', ...outputFormatFlags, params.flow_path];
|
|
223
321
|
}
|
|
224
322
|
exports.getMaestroTestCommand = getMaestroTestCommand;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
let extension;
|
|
230
|
-
switch (outputFormat) {
|
|
231
|
-
case 'junit':
|
|
232
|
-
extension = 'xml';
|
|
233
|
-
break;
|
|
234
|
-
case 'html':
|
|
235
|
-
extension = 'html';
|
|
236
|
-
break;
|
|
237
|
-
default:
|
|
238
|
-
extension = null;
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
return node_path_1.default.join(env.HOME, '.maestro', 'tests', [
|
|
242
|
-
'maestro-',
|
|
243
|
-
outputFormat,
|
|
244
|
-
'-',
|
|
245
|
-
(0, node_crypto_1.randomUUID)(),
|
|
246
|
-
// No . if no extension.
|
|
247
|
-
...(extension ? ['.', extension] : []),
|
|
248
|
-
].join(''));
|
|
249
|
-
}
|
|
323
|
+
const MaestroOutputFormatToExtensionMap = {
|
|
324
|
+
junit: 'xml',
|
|
325
|
+
html: 'html',
|
|
326
|
+
};
|
|
250
327
|
async function withCleanDeviceAsync({ platform, sourceDeviceIdentifier, localDeviceName, env, logger, fn, }) {
|
|
251
328
|
// Clone and start the device
|
|
252
329
|
let localDeviceIdentifier;
|
|
@@ -268,7 +345,7 @@ async function withCleanDeviceAsync({ platform, sourceDeviceIdentifier, localDev
|
|
|
268
345
|
udid,
|
|
269
346
|
env,
|
|
270
347
|
});
|
|
271
|
-
localDeviceIdentifier =
|
|
348
|
+
localDeviceIdentifier = localDeviceName;
|
|
272
349
|
break;
|
|
273
350
|
}
|
|
274
351
|
case 'android': {
|
|
@@ -295,17 +372,28 @@ async function withCleanDeviceAsync({ platform, sourceDeviceIdentifier, localDev
|
|
|
295
372
|
// Run the function
|
|
296
373
|
const fnResult = await (0, results_1.asyncResult)(fn({ deviceIdentifier: localDeviceIdentifier }));
|
|
297
374
|
// Stop the device
|
|
375
|
+
let logsResult = null;
|
|
298
376
|
try {
|
|
299
377
|
switch (platform) {
|
|
300
378
|
case 'ios': {
|
|
379
|
+
logger.info(`Collecting logs from ${localDeviceName}...`);
|
|
380
|
+
logsResult = await (0, results_1.asyncResult)(IosSimulatorUtils_1.IosSimulatorUtils.collectLogsAsync({
|
|
381
|
+
deviceIdentifier: localDeviceIdentifier,
|
|
382
|
+
env,
|
|
383
|
+
}));
|
|
301
384
|
logger.info(`Cleaning up ${localDeviceName}...`);
|
|
302
385
|
await IosSimulatorUtils_1.IosSimulatorUtils.deleteAsync({
|
|
303
|
-
|
|
386
|
+
deviceIdentifier: localDeviceIdentifier,
|
|
304
387
|
env,
|
|
305
388
|
});
|
|
306
389
|
break;
|
|
307
390
|
}
|
|
308
391
|
case 'android': {
|
|
392
|
+
logger.info(`Collecting logs from ${localDeviceName}...`);
|
|
393
|
+
logsResult = await (0, results_1.asyncResult)(AndroidEmulatorUtils_1.AndroidEmulatorUtils.collectLogsAsync({
|
|
394
|
+
serialId: localDeviceIdentifier,
|
|
395
|
+
env,
|
|
396
|
+
}));
|
|
309
397
|
logger.info(`Cleaning up ${localDeviceName}...`);
|
|
310
398
|
await AndroidEmulatorUtils_1.AndroidEmulatorUtils.deleteAsync({
|
|
311
399
|
serialId: localDeviceIdentifier,
|
|
@@ -318,7 +406,7 @@ async function withCleanDeviceAsync({ platform, sourceDeviceIdentifier, localDev
|
|
|
318
406
|
catch (err) {
|
|
319
407
|
logger.error(`Error cleaning up device: ${err}`);
|
|
320
408
|
}
|
|
321
|
-
return fnResult.enforceValue();
|
|
409
|
+
return { fnResult: fnResult.enforceValue(), logsResult };
|
|
322
410
|
}
|
|
323
411
|
/** Runs provided `fn` function, optionally wrapping it with starting and stopping screen recording. */
|
|
324
412
|
async function maybeWithScreenRecordingAsync({ shouldRecord, platform, deviceIdentifier, env, logger, fn, }) {
|
|
@@ -331,7 +419,7 @@ async function maybeWithScreenRecordingAsync({ shouldRecord, platform, deviceIde
|
|
|
331
419
|
switch (platform) {
|
|
332
420
|
case 'ios': {
|
|
333
421
|
recordingResult = await (0, results_1.asyncResult)(IosSimulatorUtils_1.IosSimulatorUtils.startScreenRecordingAsync({
|
|
334
|
-
|
|
422
|
+
deviceIdentifier: deviceIdentifier,
|
|
335
423
|
env,
|
|
336
424
|
}));
|
|
337
425
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internalMaestroTest.js","sourceRoot":"","sources":["../../../src/steps/functions/internalMaestroTest.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport path from 'node:path';\n\nimport {\n BuildFunction,\n BuildStepEnv,\n BuildStepInput,\n BuildStepInputValueTypeName,\n spawnAsync,\n} from '@expo/steps';\nimport { z } from 'zod';\nimport spawn, { SpawnPromise, SpawnResult } from '@expo/turtle-spawn';\nimport { PipeMode, bunyan } from '@expo/logger';\nimport { Result, asyncResult, result } from '@expo/results';\nimport { GenericArtifactType } from '@expo/eas-build-job';\n\nimport { CustomBuildContext } from '../../customBuildContext';\nimport {\n IosSimulatorName,\n IosSimulatorUtils,\n IosSimulatorUuid,\n} from '../../utils/IosSimulatorUtils';\nimport {\n AndroidDeviceSerialId,\n AndroidEmulatorUtils,\n AndroidVirtualDeviceName,\n} from '../../utils/AndroidEmulatorUtils';\n\nexport function createInternalEasMaestroTestFunction(ctx: CustomBuildContext): BuildFunction {\n return new BuildFunction({\n namespace: 'eas',\n id: '__maestro_test',\n inputProviders: [\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.STRING,\n id: 'platform',\n required: true,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.JSON,\n id: 'flow_paths',\n required: true,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.NUMBER,\n id: 'retries',\n defaultValue: 1,\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.STRING,\n id: 'include_tags',\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.STRING,\n id: 'exclude_tags',\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.NUMBER,\n id: 'shards',\n defaultValue: 1,\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.STRING,\n id: 'output_format',\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.BOOLEAN,\n id: 'record_screen',\n defaultValue: false,\n required: false,\n }),\n ],\n fn: async (stepCtx, { inputs: _inputs, env }) => {\n console.log('maestroTest', _inputs);\n // inputs come in form of { value: unknown }. Here we parse them into a typed and validated object.\n const {\n platform,\n flow_paths,\n retries,\n include_tags,\n exclude_tags,\n shards,\n output_format,\n record_screen,\n } = z\n .object({\n platform: z.enum(['ios', 'android']),\n flow_paths: z.array(z.string()),\n retries: z.number().default(1),\n include_tags: z.string().optional(),\n exclude_tags: z.string().optional(),\n shards: z.number().default(1),\n output_format: z.string().optional(),\n record_screen: z.boolean().default(false),\n })\n .parse(\n Object.fromEntries(Object.entries(_inputs).map(([key, value]) => [key, value.value]))\n );\n\n // TODO: Add support for shards. (Shouldn't be too difficult.)\n if (shards > 1) {\n stepCtx.logger.warn(\n 'Sharding support has been temporarily disabled. Running tests on a single shard.'\n );\n }\n\n let sourceDeviceIdentifier: IosSimulatorUuid | AndroidVirtualDeviceName;\n\n switch (platform) {\n case 'ios': {\n const bootedDevices = await IosSimulatorUtils.getAvailableDevicesAsync({\n env,\n filter: 'booted',\n });\n if (bootedDevices.length === 0) {\n throw new Error('No booted iOS Simulator found.');\n } else if (bootedDevices.length > 1) {\n throw new Error('Multiple booted iOS Simulators found.');\n }\n\n const device = bootedDevices[0];\n stepCtx.logger.info(`Running tests on iOS Simulator: ${device.name}.`);\n\n stepCtx.logger.info(`Preparing Simulator for tests...`);\n await spawnAsync('xcrun', ['simctl', 'shutdown', device.udid], {\n logger: stepCtx.logger,\n stdio: 'pipe',\n });\n\n sourceDeviceIdentifier = device.udid;\n break;\n }\n case 'android': {\n const connectedDevices = await AndroidEmulatorUtils.getAttachedDevicesAsync({ env });\n if (connectedDevices.length === 0) {\n throw new Error('No booted Android Emulator found.');\n } else if (connectedDevices.length > 1) {\n throw new Error('Multiple booted Android Emulators found.');\n }\n\n const { serialId } = connectedDevices[0];\n const adbEmuAvdNameResult = await spawn('adb', ['-s', serialId, 'emu', 'avd', 'name'], {\n mode: PipeMode.COMBINED,\n env,\n });\n const avdName = adbEmuAvdNameResult.stdout\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')[0] as AndroidVirtualDeviceName;\n stepCtx.logger.info(`Running tests on Android Emulator: ${avdName}.`);\n\n stepCtx.logger.info(`Preparing Emulator for tests...`);\n await spawnAsync('adb', ['-s', serialId, 'emu', 'kill'], {\n logger: stepCtx.logger,\n stdio: 'pipe',\n });\n\n sourceDeviceIdentifier = avdName;\n break;\n }\n }\n\n for (const [flowIndex, flowPath] of flow_paths.entries()) {\n for (let retryIndex = 0; retryIndex < retries; retryIndex++) {\n const localDeviceName = `eas-simulator-${flowIndex}-${retryIndex}` as\n | IosSimulatorName\n | AndroidVirtualDeviceName;\n\n // If the test passes, but the recording fails, we don't want to make the test fail,\n // so we return two separate results.\n const { fnResult, recordingResult } = await withCleanDeviceAsync({\n platform,\n sourceDeviceIdentifier,\n localDeviceName,\n env,\n logger: stepCtx.logger,\n fn: async ({ deviceIdentifier }) => {\n return await maybeWithScreenRecordingAsync({\n shouldRecord: record_screen,\n platform,\n deviceIdentifier,\n env,\n logger: stepCtx.logger,\n fn: async () => {\n const [command, ...args] = getMaestroTestCommand({\n flow_path: flowPath,\n include_tags,\n exclude_tags,\n output_format,\n output_path:\n getOutputPathForOutputFormat({\n outputFormat: output_format ?? 'noop',\n env,\n }) ?? undefined,\n });\n\n await spawnAsync(command, args, {\n logger: stepCtx.logger,\n cwd: stepCtx.workingDirectory,\n env,\n stdio: 'pipe',\n });\n },\n });\n },\n });\n\n if (recordingResult.ok && recordingResult.value) {\n try {\n await ctx.runtimeApi.uploadArtifact({\n logger: stepCtx.logger,\n artifact: {\n name: `Screen Recording (${flowPath}, retry ${retryIndex})`,\n paths: [recordingResult.value],\n type: GenericArtifactType.OTHER,\n },\n });\n } catch (err) {\n stepCtx.logger.warn('Failed to upload screen recording.', err);\n }\n }\n\n if (fnResult.ok) {\n stepCtx.logger.info(`Test passed.`);\n // Break out of the retry loop.\n break;\n }\n\n stepCtx.logger.error(`Failed to run test on device: ${fnResult.reason}`);\n stepCtx.logger.error(`Retrying...`);\n }\n }\n },\n });\n}\n\nexport function getMaestroTestCommand(params: {\n flow_path: string;\n include_tags: string | undefined;\n exclude_tags: string | undefined;\n output_format: string | undefined;\n output_path: string | undefined;\n}): [command: string, ...args: string[]] {\n let includeTagsFlag = '';\n if (typeof params.include_tags === 'string') {\n includeTagsFlag = `--include-tags=${params.include_tags}`;\n }\n\n let excludeTagsFlag = '';\n if (typeof params.exclude_tags === 'string') {\n excludeTagsFlag = `--exclude-tags=${params.exclude_tags}`;\n }\n\n let outputFormatFlags: string[] = [];\n if (params.output_format) {\n outputFormatFlags = [`--format=${params.output_format}`, `--output=${params.output_path}`];\n }\n\n return [\n 'maestro',\n 'test',\n includeTagsFlag,\n excludeTagsFlag,\n ...outputFormatFlags,\n params.flow_path,\n ].flatMap((e) => e || []) as [command: string, ...args: string[]];\n}\n\nfunction getOutputPathForOutputFormat({\n outputFormat,\n env,\n}: {\n outputFormat: string;\n env: BuildStepEnv;\n}): string | null {\n if (outputFormat.toLowerCase() === 'noop') {\n return null;\n }\n\n let extension: string | null;\n switch (outputFormat) {\n case 'junit':\n extension = 'xml';\n break;\n case 'html':\n extension = 'html';\n break;\n default:\n extension = null;\n break;\n }\n\n return path.join(\n env.HOME!,\n '.maestro',\n 'tests',\n [\n 'maestro-',\n outputFormat,\n '-',\n randomUUID(),\n // No . if no extension.\n ...(extension ? ['.', extension] : []),\n ].join('')\n );\n}\n\nasync function withCleanDeviceAsync<TResult>({\n platform,\n sourceDeviceIdentifier,\n localDeviceName,\n env,\n logger,\n fn,\n}: {\n env: BuildStepEnv;\n logger: bunyan;\n platform: 'ios' | 'android';\n sourceDeviceIdentifier: IosSimulatorUuid | AndroidVirtualDeviceName;\n localDeviceName: IosSimulatorName | AndroidVirtualDeviceName;\n fn: ({\n deviceIdentifier,\n }: {\n deviceIdentifier: IosSimulatorUuid | AndroidDeviceSerialId;\n }) => Promise<TResult>;\n}): Promise<TResult> {\n // Clone and start the device\n\n let localDeviceIdentifier: IosSimulatorUuid | AndroidDeviceSerialId;\n\n switch (platform) {\n case 'ios': {\n logger.info(`Cloning iOS Simulator ${sourceDeviceIdentifier} to ${localDeviceName}...`);\n await IosSimulatorUtils.cloneAsync({\n sourceDeviceIdentifier: sourceDeviceIdentifier as IosSimulatorUuid,\n destinationDeviceName: localDeviceName as IosSimulatorName,\n env,\n });\n logger.info(`Starting iOS Simulator ${localDeviceName}...`);\n const { udid } = await IosSimulatorUtils.startAsync({\n deviceIdentifier: localDeviceName as IosSimulatorName,\n env,\n });\n logger.info(`Waiting for iOS Simulator ${localDeviceName} to be ready...`);\n await IosSimulatorUtils.waitForReadyAsync({\n udid,\n env,\n });\n localDeviceIdentifier = udid;\n break;\n }\n case 'android': {\n logger.info(`Cloning Android Emulator ${sourceDeviceIdentifier} to ${localDeviceName}...`);\n await AndroidEmulatorUtils.cloneAsync({\n sourceDeviceName: sourceDeviceIdentifier as AndroidVirtualDeviceName,\n destinationDeviceName: localDeviceName as AndroidVirtualDeviceName,\n env,\n });\n logger.info(`Starting Android Emulator ${localDeviceName}...`);\n const { serialId } = await AndroidEmulatorUtils.startAsync({\n deviceName: localDeviceName as AndroidVirtualDeviceName,\n env,\n });\n logger.info(`Waiting for Android Emulator ${localDeviceName} to be ready...`);\n await AndroidEmulatorUtils.waitForReadyAsync({\n serialId,\n env,\n });\n localDeviceIdentifier = serialId;\n break;\n }\n }\n\n // Run the function\n\n const fnResult = await asyncResult(fn({ deviceIdentifier: localDeviceIdentifier }));\n\n // Stop the device\n\n try {\n switch (platform) {\n case 'ios': {\n logger.info(`Cleaning up ${localDeviceName}...`);\n await IosSimulatorUtils.deleteAsync({\n udid: localDeviceIdentifier as IosSimulatorUuid,\n env,\n });\n break;\n }\n case 'android': {\n logger.info(`Cleaning up ${localDeviceName}...`);\n await AndroidEmulatorUtils.deleteAsync({\n serialId: localDeviceIdentifier as AndroidDeviceSerialId,\n env,\n });\n break;\n }\n }\n } catch (err) {\n logger.error(`Error cleaning up device: ${err}`);\n }\n\n return fnResult.enforceValue();\n}\n\n/** Runs provided `fn` function, optionally wrapping it with starting and stopping screen recording. */\nasync function maybeWithScreenRecordingAsync<TResult>({\n shouldRecord,\n platform,\n deviceIdentifier,\n env,\n logger,\n fn,\n}: {\n // As weird as it is, it's more convenient to have this function like `maybeWith...`\n // than \"withScreenRecordingAsync\" and `withScreenRecordingAsync(fn)` vs `fn` in the caller.\n shouldRecord: boolean;\n platform: 'ios' | 'android';\n deviceIdentifier: IosSimulatorUuid | AndroidDeviceSerialId;\n env: BuildStepEnv;\n logger: bunyan;\n fn: () => Promise<TResult>;\n}): Promise<{ fnResult: Result<TResult>; recordingResult: Result<string | null> }> {\n if (!shouldRecord) {\n return { fnResult: await asyncResult(fn()), recordingResult: result(null) };\n }\n\n let recordingResult: Result<{\n recordingSpawn: SpawnPromise<SpawnResult>;\n outputPath?: string;\n }>;\n\n // Start screen recording\n\n logger.info(`Starting screen recording on ${deviceIdentifier}...`);\n\n switch (platform) {\n case 'ios': {\n recordingResult = await asyncResult(\n IosSimulatorUtils.startScreenRecordingAsync({\n udid: deviceIdentifier as IosSimulatorUuid,\n env,\n })\n );\n break;\n }\n case 'android': {\n recordingResult = await asyncResult(\n AndroidEmulatorUtils.startScreenRecordingAsync({\n serialId: deviceIdentifier as AndroidDeviceSerialId,\n env,\n })\n );\n break;\n }\n }\n\n if (!recordingResult.ok) {\n logger.warn('Failed to start screen recording.', recordingResult.reason);\n }\n\n // Run the function\n\n const fnResult = await asyncResult(fn());\n\n // If recording failed there's nothing to stop, so we return the results\n\n if (!recordingResult.ok) {\n return { fnResult, recordingResult: result(recordingResult.reason) };\n }\n\n // If recording started, finish it\n\n try {\n logger.info(`Stopping screen recording on ${deviceIdentifier}...`);\n\n switch (platform) {\n case 'ios': {\n await IosSimulatorUtils.stopScreenRecordingAsync({\n recordingSpawn: recordingResult.value.recordingSpawn,\n });\n return {\n fnResult,\n // We know outputPath is defined, because startIosScreenRecording() should have filled it.\n recordingResult: result(recordingResult.value.outputPath!),\n };\n }\n case 'android': {\n const { outputPath } = await AndroidEmulatorUtils.stopScreenRecordingAsync({\n serialId: deviceIdentifier as AndroidDeviceSerialId,\n recordingSpawn: recordingResult.value.recordingSpawn,\n env,\n });\n return { fnResult, recordingResult: result(outputPath) };\n }\n }\n } catch (err) {\n logger.warn('Failed to stop screen recording.', err);\n\n return { fnResult, recordingResult: result(err as Error) };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"internalMaestroTest.js","sourceRoot":"","sources":["../../../src/steps/functions/internalMaestroTest.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nimport {\n BuildFunction,\n BuildStepEnv,\n BuildStepInput,\n BuildStepInputValueTypeName,\n spawnAsync,\n} from '@expo/steps';\nimport { z } from 'zod';\nimport spawn, { SpawnPromise, SpawnResult } from '@expo/turtle-spawn';\nimport { PipeMode, bunyan } from '@expo/logger';\nimport { Result, asyncResult, result } from '@expo/results';\nimport { GenericArtifactType } from '@expo/eas-build-job';\n\nimport { CustomBuildContext } from '../../customBuildContext';\nimport { IosSimulatorName, IosSimulatorUtils } from '../../utils/IosSimulatorUtils';\nimport {\n AndroidDeviceSerialId,\n AndroidEmulatorUtils,\n AndroidVirtualDeviceName,\n} from '../../utils/AndroidEmulatorUtils';\nimport { PlatformToProperNounMap } from '../../utils/strings';\nimport { findMaestroPathsFlowsToExecuteAsync } from '../../utils/findMaestroPathsFlowsToExecuteAsync';\n\nexport function createInternalEasMaestroTestFunction(ctx: CustomBuildContext): BuildFunction {\n return new BuildFunction({\n namespace: 'eas',\n id: '__maestro_test',\n inputProviders: [\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.STRING,\n id: 'platform',\n required: true,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.JSON,\n id: 'flow_paths',\n required: true,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.NUMBER,\n id: 'retries',\n defaultValue: 1,\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.STRING,\n id: 'include_tags',\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.STRING,\n id: 'exclude_tags',\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.NUMBER,\n id: 'shards',\n defaultValue: 1,\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.STRING,\n id: 'output_format',\n required: false,\n }),\n BuildStepInput.createProvider({\n allowedValueTypeName: BuildStepInputValueTypeName.BOOLEAN,\n id: 'record_screen',\n defaultValue: false,\n required: false,\n }),\n ],\n fn: async (stepCtx, { inputs: _inputs, env }) => {\n // inputs come in form of { value: unknown }. Here we parse them into a typed and validated object.\n const {\n platform,\n flow_paths,\n retries,\n include_tags,\n exclude_tags,\n shards,\n output_format,\n record_screen,\n } = z\n .object({\n platform: z.enum(['ios', 'android']),\n flow_paths: z.array(z.string()),\n retries: z.number().default(1),\n include_tags: z.string().optional(),\n exclude_tags: z.string().optional(),\n shards: z.number().default(1),\n output_format: z.string().optional(),\n record_screen: z.boolean().default(false),\n })\n .parse(\n Object.fromEntries(Object.entries(_inputs).map(([key, value]) => [key, value.value]))\n );\n\n const flowPathsToExecute: string[] = [];\n for (const flowPath of flow_paths) {\n const flowPaths = await findMaestroPathsFlowsToExecuteAsync({\n workingDirectory: stepCtx.workingDirectory,\n flowPath,\n logger: stepCtx.logger,\n includeTags: include_tags ? include_tags.split(',') : undefined,\n excludeTags: exclude_tags ? exclude_tags.split(',') : undefined,\n });\n if (flowPaths.length === 0) {\n stepCtx.logger.warn(`No flows to execute found in \"${flowPath}\".`);\n continue;\n }\n stepCtx.logger.info(\n `Marking for execution:\\n- ${flowPaths.map((flowPath) => path.relative(stepCtx.workingDirectory, flowPath)).join('\\n- ')}`\n );\n stepCtx.logger.info('');\n flowPathsToExecute.push(...flowPaths);\n }\n\n // TODO: Add support for shards. (Shouldn't be too difficult.)\n if (shards > 1) {\n stepCtx.logger.warn(\n 'Sharding support has been temporarily disabled. Running tests on a single shard.'\n );\n }\n\n // eas/__maestro_test does not start devices, it expects a single device to be already running\n // and configured with the app. Here we find the booted device and stop it.\n\n let sourceDeviceIdentifier: IosSimulatorName | AndroidVirtualDeviceName;\n\n switch (platform) {\n case 'ios': {\n const bootedDevices = await IosSimulatorUtils.getAvailableDevicesAsync({\n env,\n filter: 'booted',\n });\n if (bootedDevices.length === 0) {\n throw new Error('No booted iOS Simulator found.');\n } else if (bootedDevices.length > 1) {\n throw new Error('Multiple booted iOS Simulators found.');\n }\n\n const device = bootedDevices[0];\n stepCtx.logger.info(`Running tests on iOS Simulator: ${device.name}.`);\n\n stepCtx.logger.info(`Preparing Simulator for tests...`);\n await spawnAsync('xcrun', ['simctl', 'shutdown', device.udid], {\n logger: stepCtx.logger,\n stdio: 'pipe',\n });\n\n sourceDeviceIdentifier = device.name;\n break;\n }\n case 'android': {\n const connectedDevices = await AndroidEmulatorUtils.getAttachedDevicesAsync({ env });\n if (connectedDevices.length === 0) {\n throw new Error('No booted Android Emulator found.');\n } else if (connectedDevices.length > 1) {\n throw new Error('Multiple booted Android Emulators found.');\n }\n\n const { serialId } = connectedDevices[0];\n const adbEmuAvdNameResult = await spawn('adb', ['-s', serialId, 'emu', 'avd', 'name'], {\n mode: PipeMode.COMBINED,\n env,\n });\n const avdName = adbEmuAvdNameResult.stdout\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')[0] as AndroidVirtualDeviceName;\n stepCtx.logger.info(`Running tests on Android Emulator: ${avdName}.`);\n\n stepCtx.logger.info(`Preparing Emulator for tests...`);\n await spawnAsync('adb', ['-s', serialId, 'emu', 'kill'], {\n stdio: 'pipe',\n });\n\n sourceDeviceIdentifier = avdName;\n break;\n }\n }\n\n // During tests we generate reports and device logs. We store them in temporary directories\n // and upload them once all tests are done. When a test is retried, new reports overwrite\n // the old ones. The files are named \"flow-${index}\" for easier identification.\n const maestroReportsDir = await fs.promises.mkdtemp(\n path.join(os.tmpdir(), 'maestro-reports-')\n );\n const deviceLogsDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'device-logs-'));\n\n const failedFlows: string[] = [];\n\n for (const [flowIndex, flowPath] of flowPathsToExecute.entries()) {\n stepCtx.logger.info('');\n\n // If output_format is empty or noop, we won't use this.\n const outputPath = path.join(\n maestroReportsDir,\n [\n `${output_format + '-' ?? ''}report-flow-${flowIndex + 1}`,\n MaestroOutputFormatToExtensionMap[output_format ?? 'noop'],\n ]\n .filter(Boolean)\n .join('.')\n );\n\n for (let attemptCount = 0; attemptCount < retries; attemptCount++) {\n const localDeviceName = `eas-simulator-${flowIndex}-${attemptCount}` as\n | IosSimulatorName\n | AndroidVirtualDeviceName;\n\n // If the test passes, but the recording fails, we don't want to make the test fail,\n // so we return two separate results.\n const {\n fnResult: { fnResult, recordingResult },\n logsResult,\n } = await withCleanDeviceAsync({\n platform,\n sourceDeviceIdentifier,\n localDeviceName,\n env,\n logger: stepCtx.logger,\n fn: async ({ deviceIdentifier }) => {\n return await maybeWithScreenRecordingAsync({\n shouldRecord: record_screen,\n platform,\n deviceIdentifier,\n env,\n logger: stepCtx.logger,\n fn: async () => {\n stepCtx.logger.info('');\n\n const [command, ...args] = getMaestroTestCommand({\n flow_path: flowPath,\n output_format,\n output_path: outputPath,\n });\n\n try {\n await spawnAsync(command, args, {\n logger: stepCtx.logger,\n cwd: stepCtx.workingDirectory,\n env,\n stdio: 'pipe',\n });\n } finally {\n stepCtx.logger.info('');\n }\n },\n });\n },\n });\n\n // Move device logs to the device logs directory.\n if (logsResult?.ok) {\n try {\n const extension = path.extname(logsResult.value.outputPath);\n const destinationPath = path.join(deviceLogsDir, `flow-${flowIndex}${extension}`);\n\n await fs.promises.rm(destinationPath, {\n force: true,\n recursive: true,\n });\n await fs.promises.rename(logsResult.value.outputPath, destinationPath);\n } catch (err) {\n stepCtx.logger.warn({ err }, 'Failed to prepare device logs for upload.');\n }\n } else if (logsResult?.reason) {\n stepCtx.logger.error({ err: logsResult.reason }, 'Failed to collect device logs.');\n }\n\n const isLastAttempt = fnResult.ok || attemptCount === retries - 1;\n if (isLastAttempt && recordingResult.value) {\n try {\n await ctx.runtimeApi.uploadArtifact({\n logger: stepCtx.logger,\n artifact: {\n // TODO(sjchmiela): Add metadata to artifacts so we don't need to encode flow path and attempt in the name.\n name: `Screen Recording (${flowPath})`,\n paths: [recordingResult.value],\n type: GenericArtifactType.OTHER,\n },\n });\n } catch (err) {\n stepCtx.logger.warn({ err }, 'Failed to upload screen recording.');\n }\n }\n\n if (fnResult.ok) {\n stepCtx.logger.info(`Test passed.`);\n // Break out of the retry loop.\n break;\n }\n\n if (attemptCount < retries - 1) {\n stepCtx.logger.info(`Retrying test...`);\n stepCtx.logger.info('');\n continue;\n }\n\n // fnResult.reason is not super interesting, but it does print out the full command so we can keep it for debugging purposes.\n stepCtx.logger.error({ err: fnResult.reason }, 'Test errored.');\n failedFlows.push(flowPath);\n }\n }\n\n stepCtx.logger.info('');\n\n // When all tests are done, we upload the reports and device logs.\n const generatedMaestroReports = await fs.promises.readdir(maestroReportsDir);\n if (generatedMaestroReports.length === 0) {\n stepCtx.logger.warn('No reports were generated.');\n } else {\n stepCtx.logger.info(`Uploading reports...`);\n try {\n await ctx.runtimeApi.uploadArtifact({\n logger: stepCtx.logger,\n artifact: {\n name: `${PlatformToProperNounMap[platform]} Maestro Test Reports (${output_format})`,\n paths: [maestroReportsDir],\n type: GenericArtifactType.OTHER,\n },\n });\n } catch (err) {\n stepCtx.logger.error({ err }, 'Failed to upload reports.');\n }\n }\n\n const generatedDeviceLogs = await fs.promises.readdir(deviceLogsDir);\n if (generatedDeviceLogs.length === 0) {\n stepCtx.logger.warn('No device logs were successfully collected.');\n } else {\n stepCtx.logger.info(`Uploading device logs...`);\n try {\n await ctx.runtimeApi.uploadArtifact({\n logger: stepCtx.logger,\n artifact: {\n name: `Maestro Test Device Logs`,\n paths: [deviceLogsDir],\n type: GenericArtifactType.OTHER,\n },\n });\n } catch (err) {\n stepCtx.logger.error({ err }, 'Failed to upload device logs.');\n }\n }\n\n stepCtx.logger.info('');\n\n // If any tests failed, we throw an error to mark the step as failed.\n if (failedFlows.length > 0) {\n throw new Error(\n `Some Maestro tests failed:\\n- ${failedFlows\n .map((flowPath) => path.relative(stepCtx.workingDirectory, flowPath))\n .join('\\n- ')}`\n );\n } else {\n stepCtx.logger.info('All Maestro tests passed.');\n }\n },\n });\n}\n\nexport function getMaestroTestCommand(params: {\n flow_path: string;\n output_format: string | undefined;\n /** Unused if `output_format` is undefined */\n output_path: string;\n}): [command: string, ...args: string[]] {\n let outputFormatFlags: string[] = [];\n if (params.output_format) {\n outputFormatFlags = [`--format`, params.output_format, `--output`, params.output_path];\n }\n\n return ['maestro', 'test', ...outputFormatFlags, params.flow_path] as [\n command: string,\n ...args: string[],\n ];\n}\n\nconst MaestroOutputFormatToExtensionMap: Record<string, string | undefined> = {\n junit: 'xml',\n html: 'html',\n};\n\nasync function withCleanDeviceAsync<TResult>({\n platform,\n sourceDeviceIdentifier,\n localDeviceName,\n env,\n logger,\n fn,\n}: {\n env: BuildStepEnv;\n logger: bunyan;\n platform: 'ios' | 'android';\n sourceDeviceIdentifier: IosSimulatorName | AndroidVirtualDeviceName;\n localDeviceName: IosSimulatorName | AndroidVirtualDeviceName;\n fn: ({\n deviceIdentifier,\n }: {\n deviceIdentifier: IosSimulatorName | AndroidDeviceSerialId;\n }) => Promise<TResult>;\n}): Promise<{ fnResult: TResult; logsResult: Result<{ outputPath: string }> | null }> {\n // Clone and start the device\n\n let localDeviceIdentifier: IosSimulatorName | AndroidDeviceSerialId;\n\n switch (platform) {\n case 'ios': {\n logger.info(`Cloning iOS Simulator ${sourceDeviceIdentifier} to ${localDeviceName}...`);\n await IosSimulatorUtils.cloneAsync({\n sourceDeviceIdentifier: sourceDeviceIdentifier as IosSimulatorName,\n destinationDeviceName: localDeviceName as IosSimulatorName,\n env,\n });\n logger.info(`Starting iOS Simulator ${localDeviceName}...`);\n const { udid } = await IosSimulatorUtils.startAsync({\n deviceIdentifier: localDeviceName as IosSimulatorName,\n env,\n });\n logger.info(`Waiting for iOS Simulator ${localDeviceName} to be ready...`);\n await IosSimulatorUtils.waitForReadyAsync({\n udid,\n env,\n });\n localDeviceIdentifier = localDeviceName as IosSimulatorName;\n break;\n }\n case 'android': {\n logger.info(`Cloning Android Emulator ${sourceDeviceIdentifier} to ${localDeviceName}...`);\n await AndroidEmulatorUtils.cloneAsync({\n sourceDeviceName: sourceDeviceIdentifier as AndroidVirtualDeviceName,\n destinationDeviceName: localDeviceName as AndroidVirtualDeviceName,\n env,\n });\n logger.info(`Starting Android Emulator ${localDeviceName}...`);\n const { serialId } = await AndroidEmulatorUtils.startAsync({\n deviceName: localDeviceName as AndroidVirtualDeviceName,\n env,\n });\n logger.info(`Waiting for Android Emulator ${localDeviceName} to be ready...`);\n await AndroidEmulatorUtils.waitForReadyAsync({\n serialId,\n env,\n });\n localDeviceIdentifier = serialId;\n break;\n }\n }\n\n // Run the function\n\n const fnResult = await asyncResult(fn({ deviceIdentifier: localDeviceIdentifier }));\n\n // Stop the device\n\n let logsResult: Result<{ outputPath: string }> | null = null;\n\n try {\n switch (platform) {\n case 'ios': {\n logger.info(`Collecting logs from ${localDeviceName}...`);\n logsResult = await asyncResult(\n IosSimulatorUtils.collectLogsAsync({\n deviceIdentifier: localDeviceIdentifier as IosSimulatorName,\n env,\n })\n );\n\n logger.info(`Cleaning up ${localDeviceName}...`);\n await IosSimulatorUtils.deleteAsync({\n deviceIdentifier: localDeviceIdentifier as IosSimulatorName,\n env,\n });\n break;\n }\n case 'android': {\n logger.info(`Collecting logs from ${localDeviceName}...`);\n logsResult = await asyncResult(\n AndroidEmulatorUtils.collectLogsAsync({\n serialId: localDeviceIdentifier as AndroidDeviceSerialId,\n env,\n })\n );\n\n logger.info(`Cleaning up ${localDeviceName}...`);\n await AndroidEmulatorUtils.deleteAsync({\n serialId: localDeviceIdentifier as AndroidDeviceSerialId,\n env,\n });\n break;\n }\n }\n } catch (err) {\n logger.error(`Error cleaning up device: ${err}`);\n }\n\n return { fnResult: fnResult.enforceValue(), logsResult };\n}\n\n/** Runs provided `fn` function, optionally wrapping it with starting and stopping screen recording. */\nasync function maybeWithScreenRecordingAsync<TResult>({\n shouldRecord,\n platform,\n deviceIdentifier,\n env,\n logger,\n fn,\n}: {\n // As weird as it is, it's more convenient to have this function like `maybeWith...`\n // than \"withScreenRecordingAsync\" and `withScreenRecordingAsync(fn)` vs `fn` in the caller.\n shouldRecord: boolean;\n platform: 'ios' | 'android';\n deviceIdentifier: IosSimulatorName | AndroidDeviceSerialId;\n env: BuildStepEnv;\n logger: bunyan;\n fn: () => Promise<TResult>;\n}): Promise<{ fnResult: Result<TResult>; recordingResult: Result<string | null> }> {\n if (!shouldRecord) {\n return { fnResult: await asyncResult(fn()), recordingResult: result(null) };\n }\n\n let recordingResult: Result<{\n recordingSpawn: SpawnPromise<SpawnResult>;\n outputPath?: string;\n }>;\n\n // Start screen recording\n\n logger.info(`Starting screen recording on ${deviceIdentifier}...`);\n\n switch (platform) {\n case 'ios': {\n recordingResult = await asyncResult(\n IosSimulatorUtils.startScreenRecordingAsync({\n deviceIdentifier: deviceIdentifier as IosSimulatorName,\n env,\n })\n );\n break;\n }\n case 'android': {\n recordingResult = await asyncResult(\n AndroidEmulatorUtils.startScreenRecordingAsync({\n serialId: deviceIdentifier as AndroidDeviceSerialId,\n env,\n })\n );\n break;\n }\n }\n\n if (!recordingResult.ok) {\n logger.warn('Failed to start screen recording.', recordingResult.reason);\n }\n\n // Run the function\n\n const fnResult = await asyncResult(fn());\n\n // If recording failed there's nothing to stop, so we return the results\n\n if (!recordingResult.ok) {\n return { fnResult, recordingResult: result(recordingResult.reason) };\n }\n\n // If recording started, finish it\n\n try {\n logger.info(`Stopping screen recording on ${deviceIdentifier}...`);\n\n switch (platform) {\n case 'ios': {\n await IosSimulatorUtils.stopScreenRecordingAsync({\n recordingSpawn: recordingResult.value.recordingSpawn,\n });\n return {\n fnResult,\n // We know outputPath is defined, because startIosScreenRecording() should have filled it.\n recordingResult: result(recordingResult.value.outputPath!),\n };\n }\n case 'android': {\n const { outputPath } = await AndroidEmulatorUtils.stopScreenRecordingAsync({\n serialId: deviceIdentifier as AndroidDeviceSerialId,\n recordingSpawn: recordingResult.value.recordingSpawn,\n env,\n });\n return { fnResult, recordingResult: result(outputPath) };\n }\n }\n } catch (err) {\n logger.warn('Failed to stop screen recording.', err);\n\n return { fnResult, recordingResult: result(err as Error) };\n }\n}\n"]}
|
|
@@ -43,6 +43,12 @@ export declare namespace AndroidEmulatorUtils {
|
|
|
43
43
|
serialId: AndroidDeviceSerialId;
|
|
44
44
|
env: NodeJS.ProcessEnv;
|
|
45
45
|
}): Promise<void>;
|
|
46
|
+
function collectLogsAsync({ serialId, env, }: {
|
|
47
|
+
serialId: AndroidDeviceSerialId;
|
|
48
|
+
env: NodeJS.ProcessEnv;
|
|
49
|
+
}): Promise<{
|
|
50
|
+
outputPath: string;
|
|
51
|
+
}>;
|
|
46
52
|
function deleteAsync({ serialId, env, }: {
|
|
47
53
|
serialId: AndroidDeviceSerialId;
|
|
48
54
|
env: NodeJS.ProcessEnv;
|
|
@@ -166,6 +166,36 @@ var AndroidEmulatorUtils;
|
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
AndroidEmulatorUtils.waitForReadyAsync = waitForReadyAsync;
|
|
169
|
+
async function collectLogsAsync({ serialId, env, }) {
|
|
170
|
+
const outputDir = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'android-emulator-logs-'));
|
|
171
|
+
const outputPath = node_path_1.default.join(outputDir, `${serialId}.log`);
|
|
172
|
+
// Pipe adb logcat output directly to the file to avoid loading it all into memory
|
|
173
|
+
await new Promise((resolve, reject) => {
|
|
174
|
+
const { child } = (0, turtle_spawn_1.default)('adb', ['-s', serialId, 'logcat', '-d'], {
|
|
175
|
+
env,
|
|
176
|
+
stdio: ['ignore', 'pipe', 'inherit'],
|
|
177
|
+
});
|
|
178
|
+
if (!child.stdout) {
|
|
179
|
+
reject(new Error('"adb logcat" did not start correctly.'));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const writeStream = node_fs_1.default.createWriteStream(outputPath);
|
|
183
|
+
child.stdout.pipe(writeStream);
|
|
184
|
+
child.stdout.on('error', reject);
|
|
185
|
+
child.on('error', reject);
|
|
186
|
+
writeStream.on('error', reject);
|
|
187
|
+
child.on('close', (code) => {
|
|
188
|
+
if (code === 0) {
|
|
189
|
+
resolve();
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
reject(new Error(`"adb logcat" exited with code ${code}`));
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
return { outputPath };
|
|
197
|
+
}
|
|
198
|
+
AndroidEmulatorUtils.collectLogsAsync = collectLogsAsync;
|
|
169
199
|
async function deleteAsync({ serialId, env, }) {
|
|
170
200
|
const adbEmuAvdName = await (0, turtle_spawn_1.default)('adb', ['-s', serialId, 'emu', 'avd', 'name'], {
|
|
171
201
|
env,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AndroidEmulatorUtils.js","sourceRoot":"","sources":["../../src/utils/AndroidEmulatorUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'assert';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport { setTimeout } from 'node:timers/promises';\nimport path from 'node:path';\n\n// import { PipeMode } from '@expo/logger';\nimport spawn, { SpawnPromise, SpawnResult } from '@expo/turtle-spawn';\nimport { z } from 'zod';\n\nimport { retryAsync } from './retry';\n\n/** Android Virtual Device is the device we run. */\nexport type AndroidVirtualDeviceName = string & z.BRAND<'AndroidVirtualDeviceName'>;\n/** Android device is configuration for the AVD -- screen size, etc. */\nexport type AndroidDeviceName = string & z.BRAND<'AndroidDeviceName'>;\nexport type AndroidDeviceSerialId = string & z.BRAND<'AndroidDeviceSerialId'>;\n\nexport namespace AndroidEmulatorUtils {\n export const defaultSystemImagePackage = `system-images;android-30;default;${\n process.arch === 'arm64' ? 'arm64-v8a' : 'x86_64'\n }`;\n\n export async function getAvailableDevicesAsync({\n env,\n }: {\n env: NodeJS.ProcessEnv;\n }): Promise<AndroidDeviceName[]> {\n const result = await spawn('avdmanager', ['list', 'device', '--compact', '--null'], { env });\n return result.stdout.split('\\0').filter((line) => line !== '') as AndroidDeviceName[];\n }\n\n export async function getAttachedDevicesAsync({\n env,\n }: {\n env: NodeJS.ProcessEnv;\n }): Promise<{ serialId: AndroidDeviceSerialId; state: 'offline' | 'device' }[]> {\n const result = await spawn('adb', ['devices', '-l'], {\n env,\n });\n return result.stdout\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')\n .filter((line) => line.startsWith('emulator'))\n .map((line) => {\n const [serialId, state] = line.split(/\\s+/) as [\n AndroidDeviceSerialId,\n 'offline' | 'device',\n ];\n return {\n serialId,\n state,\n };\n });\n }\n\n export async function getSerialIdAsync({\n deviceName,\n env,\n }: {\n deviceName: AndroidVirtualDeviceName;\n env: NodeJS.ProcessEnv;\n }): Promise<AndroidDeviceSerialId | null> {\n const adbDevices = await spawn('adb', ['devices'], { env });\n for (const adbDeviceLine of adbDevices.stdout.split('\\n')) {\n if (!adbDeviceLine.startsWith('emulator')) {\n continue;\n }\n\n const matches = adbDeviceLine.match(/^(\\S+)/);\n if (!matches) {\n continue;\n }\n\n const [, serialId] = matches;\n // Previously we were using `qemu.uuid` to identify the emulator,\n // but this does not work for newer emulators, because there is\n // a limit on properties and custom properties get ignored.\n // See https://stackoverflow.com/questions/2214377/how-to-get-serial-number-or-id-of-android-emulator-after-it-runs#comment98259121_42038655\n const adbEmuAvdName = await spawn('adb', ['-s', serialId, 'emu', 'avd', 'name'], {\n env,\n });\n if (adbEmuAvdName.stdout.replace(/\\r\\n/g, '\\n').split('\\n')[0] === deviceName) {\n return serialId as AndroidDeviceSerialId;\n }\n }\n\n return null;\n }\n\n export async function createAsync({\n deviceName,\n systemImagePackage,\n deviceIdentifier,\n env,\n }: {\n deviceName: AndroidVirtualDeviceName;\n systemImagePackage: string;\n deviceIdentifier: AndroidDeviceName | null;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n const avdManager = spawn(\n 'avdmanager',\n [\n 'create',\n 'avd',\n '--name',\n deviceName,\n '--package',\n systemImagePackage,\n '--force',\n ...(deviceIdentifier ? ['--device', deviceIdentifier] : []),\n ],\n {\n env,\n stdio: 'pipe',\n }\n );\n // `avdmanager create` always asks about creating a custom hardware profile.\n // > Do you wish to create a custom hardware profile? [no]\n // We answer \"no\".\n avdManager.child.stdin?.write('no');\n avdManager.child.stdin?.end();\n await avdManager;\n }\n\n export async function cloneAsync({\n sourceDeviceName,\n destinationDeviceName,\n env,\n }: {\n sourceDeviceName: AndroidVirtualDeviceName;\n destinationDeviceName: AndroidVirtualDeviceName;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n const cloneIniFile = `${env.HOME}/.android/avd/${destinationDeviceName}.ini`;\n\n await fs.promises.rm(`${env.HOME}/.android/avd/${destinationDeviceName}.avd`, {\n recursive: true,\n force: true,\n });\n await fs.promises.rm(cloneIniFile, { force: true });\n\n await fs.promises.cp(\n `${env.HOME}/.android/avd/${sourceDeviceName}.avd`,\n `${env.HOME}/.android/avd/${destinationDeviceName}.avd`,\n { recursive: true, verbatimSymlinks: true, force: true }\n );\n\n await fs.promises.cp(`${env.HOME}/.android/avd/${sourceDeviceName}.ini`, cloneIniFile, {\n verbatimSymlinks: true,\n force: true,\n });\n\n const filesToReplaceDeviceNameIn = // TODO: Test whether we need to use `spawnAsync` here.\n (\n await spawn('grep', [\n '--binary-files=without-match',\n '--recursive',\n '--files-with-matches',\n `${sourceDeviceName}`,\n `${env.HOME}/.android/avd/${destinationDeviceName}.avd`,\n ])\n ).stdout\n .split('\\n')\n .filter((file) => file !== '');\n\n for (const file of [...filesToReplaceDeviceNameIn, cloneIniFile]) {\n const txtFile = await fs.promises.readFile(file, 'utf-8');\n const replaceRegex = new RegExp(`${sourceDeviceName}`, 'g');\n const updatedTxtFile = txtFile.replace(replaceRegex, destinationDeviceName);\n await fs.promises.writeFile(file, updatedTxtFile);\n }\n }\n\n export async function startAsync({\n deviceName,\n env,\n }: {\n deviceName: AndroidVirtualDeviceName;\n env: NodeJS.ProcessEnv;\n }): Promise<{ emulatorPromise: SpawnPromise<SpawnResult>; serialId: AndroidDeviceSerialId }> {\n const emulatorPromise = spawn(\n `${process.env.ANDROID_HOME}/emulator/emulator`,\n [\n '-no-window',\n '-no-boot-anim',\n '-writable-system',\n '-noaudio',\n '-memory',\n '8192',\n '-no-snapshot-save',\n '-avd',\n deviceName,\n ],\n {\n detached: true,\n stdio: 'inherit',\n env,\n }\n );\n // If emulator fails to start, throw its error.\n if (!emulatorPromise.child.pid) {\n await emulatorPromise;\n }\n emulatorPromise.child.unref();\n\n const serialId = await retryAsync(\n async () => {\n const serialId = await getSerialIdAsync({ deviceName, env });\n assert(\n serialId,\n `Failed to configure emulator (${serialId}): emulator with required ID not found.`\n );\n return serialId;\n },\n {\n retryOptions: {\n retries: 3 * 60,\n retryIntervalMs: 1_000,\n },\n }\n );\n\n // We don't want to await the SpawnPromise here.\n // eslint-disable-next-line @typescript-eslint/return-await\n return { emulatorPromise, serialId };\n }\n\n export async function waitForReadyAsync({\n serialId,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n await retryAsync(\n async () => {\n const { stdout } = await spawn(\n 'adb',\n ['-s', serialId, 'shell', 'getprop', 'sys.boot_completed'],\n { env }\n );\n\n if (!stdout.startsWith('1')) {\n throw new Error(`Emulator (${serialId}) boot has not completed.`);\n }\n },\n {\n // Retry every second for 3 minutes.\n retryOptions: {\n retries: 3 * 60,\n retryIntervalMs: 1_000,\n },\n }\n );\n }\n\n export async function deleteAsync({\n serialId,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n const adbEmuAvdName = await spawn('adb', ['-s', serialId, 'emu', 'avd', 'name'], {\n env,\n });\n const deviceName = adbEmuAvdName.stdout.replace(/\\r\\n/g, '\\n').split('\\n')[0];\n\n await spawn('adb', ['-s', serialId, 'emu', 'kill'], { env });\n\n await retryAsync(\n async () => {\n const devices = await getAttachedDevicesAsync({ env });\n if (devices.some((device) => device.serialId === serialId)) {\n throw new Error(`Emulator (${serialId}) is still attached.`);\n }\n },\n {\n retryOptions: {\n retries: 3 * 60,\n retryIntervalMs: 1_000,\n },\n }\n );\n\n await spawn('avdmanager', ['delete', 'avd', '-n', deviceName], { env });\n }\n\n export async function startScreenRecordingAsync({\n serialId,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n env: NodeJS.ProcessEnv;\n }): Promise<{\n recordingSpawn: SpawnPromise<SpawnResult>;\n }> {\n let isReady = false;\n\n // Ensure /sdcard/ is ready to write to. (If the emulator was just booted, it might not be ready yet.)\n for (let i = 0; i < 30; i++) {\n try {\n await spawn('adb', ['-s', serialId, 'shell', 'touch', '/sdcard/.expo-recording-ready'], {\n env,\n });\n isReady = true;\n break;\n } catch {\n await setTimeout(1000);\n }\n }\n\n if (!isReady) {\n throw new Error(`Emulator (${serialId}) filesystem was not ready in time.`);\n }\n\n const screenrecordArgs = [\n '-s',\n serialId,\n 'shell',\n 'screenrecord',\n '--verbose',\n '/sdcard/expo-recording.mp4',\n ];\n\n const screenrecordHelp = await spawn(\n 'adb',\n ['-s', serialId, 'shell', 'screenrecord', '--help'],\n {\n env,\n }\n );\n\n if (screenrecordHelp.stdout.includes('remove the time limit')) {\n screenrecordArgs.push('--time-limit', '0');\n }\n\n const recordingSpawn = spawn('adb', screenrecordArgs, {\n env,\n stdio: 'pipe',\n });\n recordingSpawn.child.unref();\n\n // We are returning the SpawnPromise here, so we don't await it.\n // eslint-disable-next-line @typescript-eslint/return-await\n return {\n recordingSpawn,\n };\n }\n\n export async function stopScreenRecordingAsync({\n serialId,\n recordingSpawn,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n recordingSpawn: SpawnPromise<SpawnResult>;\n env: NodeJS.ProcessEnv;\n }): Promise<{ outputPath: string }> {\n recordingSpawn.child.kill(1);\n\n try {\n await recordingSpawn;\n } catch {\n // do nothing\n }\n\n let isRecordingBusy = true;\n for (let i = 0; i < 30; i++) {\n const lsof = await spawn(\n 'adb',\n ['-s', serialId, 'shell', 'lsof -t /sdcard/expo-recording.mp4'],\n { env }\n );\n if (lsof.stdout.trim() === '') {\n isRecordingBusy = false;\n break;\n }\n await setTimeout(1000);\n }\n\n if (isRecordingBusy) {\n throw new Error(`Recording file is busy.`);\n }\n\n const outputDir = await fs.promises.mkdtemp(\n path.join(os.tmpdir(), 'android-screen-recording-')\n );\n const outputPath = path.join(outputDir, `${serialId}.mp4`);\n\n await spawn('adb', ['-s', serialId, 'pull', '/sdcard/expo-recording.mp4', outputPath], { env });\n\n return { outputPath };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AndroidEmulatorUtils.js","sourceRoot":"","sources":["../../src/utils/AndroidEmulatorUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'assert';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport { setTimeout } from 'node:timers/promises';\nimport path from 'node:path';\n\n// import { PipeMode } from '@expo/logger';\nimport spawn, { SpawnPromise, SpawnResult } from '@expo/turtle-spawn';\nimport { z } from 'zod';\n\nimport { retryAsync } from './retry';\n\n/** Android Virtual Device is the device we run. */\nexport type AndroidVirtualDeviceName = string & z.BRAND<'AndroidVirtualDeviceName'>;\n/** Android device is configuration for the AVD -- screen size, etc. */\nexport type AndroidDeviceName = string & z.BRAND<'AndroidDeviceName'>;\nexport type AndroidDeviceSerialId = string & z.BRAND<'AndroidDeviceSerialId'>;\n\nexport namespace AndroidEmulatorUtils {\n export const defaultSystemImagePackage = `system-images;android-30;default;${\n process.arch === 'arm64' ? 'arm64-v8a' : 'x86_64'\n }`;\n\n export async function getAvailableDevicesAsync({\n env,\n }: {\n env: NodeJS.ProcessEnv;\n }): Promise<AndroidDeviceName[]> {\n const result = await spawn('avdmanager', ['list', 'device', '--compact', '--null'], { env });\n return result.stdout.split('\\0').filter((line) => line !== '') as AndroidDeviceName[];\n }\n\n export async function getAttachedDevicesAsync({\n env,\n }: {\n env: NodeJS.ProcessEnv;\n }): Promise<{ serialId: AndroidDeviceSerialId; state: 'offline' | 'device' }[]> {\n const result = await spawn('adb', ['devices', '-l'], {\n env,\n });\n return result.stdout\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')\n .filter((line) => line.startsWith('emulator'))\n .map((line) => {\n const [serialId, state] = line.split(/\\s+/) as [\n AndroidDeviceSerialId,\n 'offline' | 'device',\n ];\n return {\n serialId,\n state,\n };\n });\n }\n\n export async function getSerialIdAsync({\n deviceName,\n env,\n }: {\n deviceName: AndroidVirtualDeviceName;\n env: NodeJS.ProcessEnv;\n }): Promise<AndroidDeviceSerialId | null> {\n const adbDevices = await spawn('adb', ['devices'], { env });\n for (const adbDeviceLine of adbDevices.stdout.split('\\n')) {\n if (!adbDeviceLine.startsWith('emulator')) {\n continue;\n }\n\n const matches = adbDeviceLine.match(/^(\\S+)/);\n if (!matches) {\n continue;\n }\n\n const [, serialId] = matches;\n // Previously we were using `qemu.uuid` to identify the emulator,\n // but this does not work for newer emulators, because there is\n // a limit on properties and custom properties get ignored.\n // See https://stackoverflow.com/questions/2214377/how-to-get-serial-number-or-id-of-android-emulator-after-it-runs#comment98259121_42038655\n const adbEmuAvdName = await spawn('adb', ['-s', serialId, 'emu', 'avd', 'name'], {\n env,\n });\n if (adbEmuAvdName.stdout.replace(/\\r\\n/g, '\\n').split('\\n')[0] === deviceName) {\n return serialId as AndroidDeviceSerialId;\n }\n }\n\n return null;\n }\n\n export async function createAsync({\n deviceName,\n systemImagePackage,\n deviceIdentifier,\n env,\n }: {\n deviceName: AndroidVirtualDeviceName;\n systemImagePackage: string;\n deviceIdentifier: AndroidDeviceName | null;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n const avdManager = spawn(\n 'avdmanager',\n [\n 'create',\n 'avd',\n '--name',\n deviceName,\n '--package',\n systemImagePackage,\n '--force',\n ...(deviceIdentifier ? ['--device', deviceIdentifier] : []),\n ],\n {\n env,\n stdio: 'pipe',\n }\n );\n // `avdmanager create` always asks about creating a custom hardware profile.\n // > Do you wish to create a custom hardware profile? [no]\n // We answer \"no\".\n avdManager.child.stdin?.write('no');\n avdManager.child.stdin?.end();\n await avdManager;\n }\n\n export async function cloneAsync({\n sourceDeviceName,\n destinationDeviceName,\n env,\n }: {\n sourceDeviceName: AndroidVirtualDeviceName;\n destinationDeviceName: AndroidVirtualDeviceName;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n const cloneIniFile = `${env.HOME}/.android/avd/${destinationDeviceName}.ini`;\n\n await fs.promises.rm(`${env.HOME}/.android/avd/${destinationDeviceName}.avd`, {\n recursive: true,\n force: true,\n });\n await fs.promises.rm(cloneIniFile, { force: true });\n\n await fs.promises.cp(\n `${env.HOME}/.android/avd/${sourceDeviceName}.avd`,\n `${env.HOME}/.android/avd/${destinationDeviceName}.avd`,\n { recursive: true, verbatimSymlinks: true, force: true }\n );\n\n await fs.promises.cp(`${env.HOME}/.android/avd/${sourceDeviceName}.ini`, cloneIniFile, {\n verbatimSymlinks: true,\n force: true,\n });\n\n const filesToReplaceDeviceNameIn = // TODO: Test whether we need to use `spawnAsync` here.\n (\n await spawn('grep', [\n '--binary-files=without-match',\n '--recursive',\n '--files-with-matches',\n `${sourceDeviceName}`,\n `${env.HOME}/.android/avd/${destinationDeviceName}.avd`,\n ])\n ).stdout\n .split('\\n')\n .filter((file) => file !== '');\n\n for (const file of [...filesToReplaceDeviceNameIn, cloneIniFile]) {\n const txtFile = await fs.promises.readFile(file, 'utf-8');\n const replaceRegex = new RegExp(`${sourceDeviceName}`, 'g');\n const updatedTxtFile = txtFile.replace(replaceRegex, destinationDeviceName);\n await fs.promises.writeFile(file, updatedTxtFile);\n }\n }\n\n export async function startAsync({\n deviceName,\n env,\n }: {\n deviceName: AndroidVirtualDeviceName;\n env: NodeJS.ProcessEnv;\n }): Promise<{ emulatorPromise: SpawnPromise<SpawnResult>; serialId: AndroidDeviceSerialId }> {\n const emulatorPromise = spawn(\n `${process.env.ANDROID_HOME}/emulator/emulator`,\n [\n '-no-window',\n '-no-boot-anim',\n '-writable-system',\n '-noaudio',\n '-memory',\n '8192',\n '-no-snapshot-save',\n '-avd',\n deviceName,\n ],\n {\n detached: true,\n stdio: 'inherit',\n env,\n }\n );\n // If emulator fails to start, throw its error.\n if (!emulatorPromise.child.pid) {\n await emulatorPromise;\n }\n emulatorPromise.child.unref();\n\n const serialId = await retryAsync(\n async () => {\n const serialId = await getSerialIdAsync({ deviceName, env });\n assert(\n serialId,\n `Failed to configure emulator (${serialId}): emulator with required ID not found.`\n );\n return serialId;\n },\n {\n retryOptions: {\n retries: 3 * 60,\n retryIntervalMs: 1_000,\n },\n }\n );\n\n // We don't want to await the SpawnPromise here.\n // eslint-disable-next-line @typescript-eslint/return-await\n return { emulatorPromise, serialId };\n }\n\n export async function waitForReadyAsync({\n serialId,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n await retryAsync(\n async () => {\n const { stdout } = await spawn(\n 'adb',\n ['-s', serialId, 'shell', 'getprop', 'sys.boot_completed'],\n { env }\n );\n\n if (!stdout.startsWith('1')) {\n throw new Error(`Emulator (${serialId}) boot has not completed.`);\n }\n },\n {\n // Retry every second for 3 minutes.\n retryOptions: {\n retries: 3 * 60,\n retryIntervalMs: 1_000,\n },\n }\n );\n }\n\n export async function collectLogsAsync({\n serialId,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n env: NodeJS.ProcessEnv;\n }): Promise<{ outputPath: string }> {\n const outputDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'android-emulator-logs-'));\n const outputPath = path.join(outputDir, `${serialId}.log`);\n\n // Pipe adb logcat output directly to the file to avoid loading it all into memory\n await new Promise<void>((resolve, reject) => {\n const { child } = spawn('adb', ['-s', serialId, 'logcat', '-d'], {\n env,\n stdio: ['ignore', 'pipe', 'inherit'],\n });\n\n if (!child.stdout) {\n reject(new Error('\"adb logcat\" did not start correctly.'));\n return;\n }\n\n const writeStream = fs.createWriteStream(outputPath);\n child.stdout.pipe(writeStream);\n child.stdout.on('error', reject);\n\n child.on('error', reject);\n writeStream.on('error', reject);\n\n child.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`\"adb logcat\" exited with code ${code}`));\n }\n });\n });\n\n return { outputPath };\n }\n\n export async function deleteAsync({\n serialId,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n const adbEmuAvdName = await spawn('adb', ['-s', serialId, 'emu', 'avd', 'name'], {\n env,\n });\n const deviceName = adbEmuAvdName.stdout.replace(/\\r\\n/g, '\\n').split('\\n')[0];\n\n await spawn('adb', ['-s', serialId, 'emu', 'kill'], { env });\n\n await retryAsync(\n async () => {\n const devices = await getAttachedDevicesAsync({ env });\n if (devices.some((device) => device.serialId === serialId)) {\n throw new Error(`Emulator (${serialId}) is still attached.`);\n }\n },\n {\n retryOptions: {\n retries: 3 * 60,\n retryIntervalMs: 1_000,\n },\n }\n );\n\n await spawn('avdmanager', ['delete', 'avd', '-n', deviceName], { env });\n }\n\n export async function startScreenRecordingAsync({\n serialId,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n env: NodeJS.ProcessEnv;\n }): Promise<{\n recordingSpawn: SpawnPromise<SpawnResult>;\n }> {\n let isReady = false;\n\n // Ensure /sdcard/ is ready to write to. (If the emulator was just booted, it might not be ready yet.)\n for (let i = 0; i < 30; i++) {\n try {\n await spawn('adb', ['-s', serialId, 'shell', 'touch', '/sdcard/.expo-recording-ready'], {\n env,\n });\n isReady = true;\n break;\n } catch {\n await setTimeout(1000);\n }\n }\n\n if (!isReady) {\n throw new Error(`Emulator (${serialId}) filesystem was not ready in time.`);\n }\n\n const screenrecordArgs = [\n '-s',\n serialId,\n 'shell',\n 'screenrecord',\n '--verbose',\n '/sdcard/expo-recording.mp4',\n ];\n\n const screenrecordHelp = await spawn(\n 'adb',\n ['-s', serialId, 'shell', 'screenrecord', '--help'],\n {\n env,\n }\n );\n\n if (screenrecordHelp.stdout.includes('remove the time limit')) {\n screenrecordArgs.push('--time-limit', '0');\n }\n\n const recordingSpawn = spawn('adb', screenrecordArgs, {\n env,\n stdio: 'pipe',\n });\n recordingSpawn.child.unref();\n\n // We are returning the SpawnPromise here, so we don't await it.\n // eslint-disable-next-line @typescript-eslint/return-await\n return {\n recordingSpawn,\n };\n }\n\n export async function stopScreenRecordingAsync({\n serialId,\n recordingSpawn,\n env,\n }: {\n serialId: AndroidDeviceSerialId;\n recordingSpawn: SpawnPromise<SpawnResult>;\n env: NodeJS.ProcessEnv;\n }): Promise<{ outputPath: string }> {\n recordingSpawn.child.kill(1);\n\n try {\n await recordingSpawn;\n } catch {\n // do nothing\n }\n\n let isRecordingBusy = true;\n for (let i = 0; i < 30; i++) {\n const lsof = await spawn(\n 'adb',\n ['-s', serialId, 'shell', 'lsof -t /sdcard/expo-recording.mp4'],\n { env }\n );\n if (lsof.stdout.trim() === '') {\n isRecordingBusy = false;\n break;\n }\n await setTimeout(1000);\n }\n\n if (isRecordingBusy) {\n throw new Error(`Recording file is busy.`);\n }\n\n const outputDir = await fs.promises.mkdtemp(\n path.join(os.tmpdir(), 'android-screen-recording-')\n );\n const outputPath = path.join(outputDir, `${serialId}.mp4`);\n\n await spawn('adb', ['-s', serialId, 'pull', '/sdcard/expo-recording.mp4', outputPath], { env });\n\n return { outputPath };\n }\n}\n"]}
|
|
@@ -50,12 +50,18 @@ export declare namespace IosSimulatorUtils {
|
|
|
50
50
|
udid: IosSimulatorUuid;
|
|
51
51
|
env: NodeJS.ProcessEnv;
|
|
52
52
|
}): Promise<void>;
|
|
53
|
-
export function
|
|
54
|
-
|
|
53
|
+
export function collectLogsAsync({ deviceIdentifier, env, }: {
|
|
54
|
+
deviceIdentifier: IosSimulatorName | IosSimulatorUuid;
|
|
55
|
+
env: NodeJS.ProcessEnv;
|
|
56
|
+
}): Promise<{
|
|
57
|
+
outputPath: string;
|
|
58
|
+
}>;
|
|
59
|
+
export function deleteAsync({ deviceIdentifier, env, }: {
|
|
60
|
+
deviceIdentifier: IosSimulatorName | IosSimulatorUuid;
|
|
55
61
|
env: NodeJS.ProcessEnv;
|
|
56
62
|
}): Promise<void>;
|
|
57
|
-
export function startScreenRecordingAsync({
|
|
58
|
-
|
|
63
|
+
export function startScreenRecordingAsync({ deviceIdentifier, env, }: {
|
|
64
|
+
deviceIdentifier: IosSimulatorUuid | IosSimulatorName;
|
|
59
65
|
env: NodeJS.ProcessEnv;
|
|
60
66
|
}): Promise<{
|
|
61
67
|
recordingSpawn: SpawnPromise<SpawnResult>;
|
|
@@ -63,15 +63,24 @@ var IosSimulatorUtils;
|
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
IosSimulatorUtils.waitForReadyAsync = waitForReadyAsync;
|
|
66
|
-
async function
|
|
67
|
-
await (
|
|
68
|
-
|
|
66
|
+
async function collectLogsAsync({ deviceIdentifier, env, }) {
|
|
67
|
+
const outputDir = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'ios-simulator-logs-'));
|
|
68
|
+
const outputPath = node_path_1.default.join(outputDir, `${deviceIdentifier}.logarchive`);
|
|
69
|
+
await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'spawn', deviceIdentifier, 'log', 'collect', '--output', outputPath], {
|
|
70
|
+
env,
|
|
71
|
+
});
|
|
72
|
+
return { outputPath };
|
|
73
|
+
}
|
|
74
|
+
IosSimulatorUtils.collectLogsAsync = collectLogsAsync;
|
|
75
|
+
async function deleteAsync({ deviceIdentifier, env, }) {
|
|
76
|
+
await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'shutdown', deviceIdentifier], { env });
|
|
77
|
+
await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'delete', deviceIdentifier], { env });
|
|
69
78
|
}
|
|
70
79
|
IosSimulatorUtils.deleteAsync = deleteAsync;
|
|
71
|
-
async function startScreenRecordingAsync({
|
|
80
|
+
async function startScreenRecordingAsync({ deviceIdentifier, env, }) {
|
|
72
81
|
const outputDir = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'ios-screen-recording-'));
|
|
73
|
-
const outputPath = node_path_1.default.join(outputDir, `${
|
|
74
|
-
const recordingSpawn = (0, turtle_spawn_1.default)('xcrun', ['simctl', 'io',
|
|
82
|
+
const outputPath = node_path_1.default.join(outputDir, `${deviceIdentifier}.mov`);
|
|
83
|
+
const recordingSpawn = (0, turtle_spawn_1.default)('xcrun', ['simctl', 'io', deviceIdentifier, 'recordVideo', '-f', outputPath], {
|
|
75
84
|
env,
|
|
76
85
|
});
|
|
77
86
|
const stdout = recordingSpawn.child.stdout;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IosSimulatorUtils.js","sourceRoot":"","sources":["../../src/utils/IosSimulatorUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport os from 'node:os';\nimport fs from 'node:fs';\nimport { setTimeout } from 'node:timers/promises';\n\nimport spawn, { SpawnPromise, SpawnResult } from '@expo/turtle-spawn';\nimport { z } from 'zod';\n\nimport { retryAsync } from './retry';\n\nexport type IosSimulatorUuid = string & z.BRAND<'IosSimulatorUuid'>;\nexport type IosSimulatorName = string & z.BRAND<'IosSimulatorName'>;\n\nexport namespace IosSimulatorUtils {\n type XcrunSimctlDevice = {\n availabilityError?: string;\n /** e.g. /Users/sjchmiela/Library/Developer/CoreSimulator/Devices/8272DEB1-42B5-4F78-AB2D-0BC5F320B822/data */\n dataPath: string;\n /** e.g. 18341888 */\n dataPathSize: number;\n /** e.g. /Users/sjchmiela/Library/Logs/CoreSimulator/8272DEB1-42B5-4F78-AB2D-0BC5F320B822 */\n logPath: string;\n /** e.g. 8272DEB1-42B5-4F78-AB2D-0BC5F320B822 */\n udid: IosSimulatorUuid;\n isAvailable: boolean;\n /** e.g. com.apple.CoreSimulator.SimDeviceType.iPhone-13-mini */\n deviceTypeIdentifier: string;\n state: 'Shutdown' | 'Booted';\n /** e.g. iPhone 15 */\n name: IosSimulatorName;\n /** e.g. 2024-01-22T19:28:56Z */\n lastBootedAt?: string;\n };\n\n type SimulatorDevice = XcrunSimctlDevice & { runtime: string; displayName: string };\n\n type XcrunSimctlListDevicesJsonOutput = {\n devices: {\n [runtime: string]: XcrunSimctlDevice[];\n };\n };\n\n export async function getAvailableDevicesAsync({\n env,\n filter,\n }: {\n env: NodeJS.ProcessEnv;\n filter: 'available' | 'booted';\n }): Promise<SimulatorDevice[]> {\n const result = await spawn(\n 'xcrun',\n ['simctl', 'list', 'devices', '--json', '--no-escape-slashes', filter],\n { env }\n );\n const xcrunData = JSON.parse(result.stdout) as XcrunSimctlListDevicesJsonOutput;\n\n const allAvailableDevices: SimulatorDevice[] = [];\n for (const [runtime, devices] of Object.entries(xcrunData.devices)) {\n allAvailableDevices.push(\n ...devices.map((device) => ({\n ...device,\n runtime,\n displayName: `${device.name} (${device.udid}) on ${runtime}`,\n }))\n );\n }\n\n return allAvailableDevices;\n }\n\n export async function getDeviceAsync({\n udid,\n env,\n }: {\n env: NodeJS.ProcessEnv;\n udid: IosSimulatorUuid;\n }): Promise<SimulatorDevice | null> {\n const devices = await getAvailableDevicesAsync({ env, filter: 'available' });\n return devices.find((device) => device.udid === udid) ?? null;\n }\n\n export async function cloneAsync({\n sourceDeviceIdentifier,\n destinationDeviceName,\n env,\n }: {\n sourceDeviceIdentifier: IosSimulatorName | IosSimulatorUuid;\n destinationDeviceName: IosSimulatorName;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n await spawn('xcrun', ['simctl', 'clone', sourceDeviceIdentifier, destinationDeviceName], {\n env,\n });\n }\n\n export async function startAsync({\n deviceIdentifier,\n env,\n }: {\n deviceIdentifier: IosSimulatorUuid | IosSimulatorName;\n env: NodeJS.ProcessEnv;\n }): Promise<{ udid: IosSimulatorUuid }> {\n const bootstatusResult = await spawn(\n 'xcrun',\n ['simctl', 'bootstatus', deviceIdentifier, '-b'],\n {\n env,\n }\n );\n\n const udid = parseUdidFromBootstatusStdout(bootstatusResult.stdout);\n if (!udid) {\n throw new Error('Failed to parse UDID from bootstatus result.');\n }\n\n return { udid };\n }\n\n export async function waitForReadyAsync({\n udid,\n env,\n }: {\n udid: IosSimulatorUuid;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n await retryAsync(\n async () => {\n await spawn('xcrun', ['simctl', 'io', udid, 'screenshot', '/dev/null'], {\n env,\n });\n },\n {\n retryOptions: {\n // There's 30 * 60 seconds in 30 minutes, which is the timeout.\n retries: 30 * 60,\n retryIntervalMs: 1_000,\n },\n }\n );\n }\n\n export async function deleteAsync({\n udid,\n env,\n }: {\n udid: IosSimulatorUuid;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n await spawn('xcrun', ['simctl', 'shutdown', udid], { env });\n await spawn('xcrun', ['simctl', 'delete', udid], { env });\n }\n\n export async function startScreenRecordingAsync({\n udid,\n env,\n }: {\n udid: IosSimulatorUuid;\n env: NodeJS.ProcessEnv;\n }): Promise<{\n recordingSpawn: SpawnPromise<SpawnResult>;\n outputPath: string;\n }> {\n const outputDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'ios-screen-recording-'));\n const outputPath = path.join(outputDir, `${udid}.mov`);\n const recordingSpawn = spawn('xcrun', ['simctl', 'io', udid, 'recordVideo', '-f', outputPath], {\n env,\n });\n\n const stdout = recordingSpawn.child.stdout;\n const stderr = recordingSpawn.child.stderr;\n if (!stdout || !stderr) {\n // No stdout/stderr means the process failed to start, so awaiting it will throw an error.\n await recordingSpawn;\n throw new Error('Recording process failed to start.');\n }\n\n let outputAggregated = '';\n\n // Listen to both stdout and stderr since \"Recording started\" might come from either\n stdout.on('data', (data) => {\n const output = data.toString();\n outputAggregated += output;\n });\n\n stderr.on('data', (data) => {\n const output = data.toString();\n outputAggregated += output;\n });\n\n let isRecordingStarted = false;\n for (let i = 0; i < 20; i++) {\n // Check if recording started message appears in either stdout or stderr\n if (outputAggregated.includes('Recording started')) {\n isRecordingStarted = true;\n break;\n }\n await setTimeout(1000);\n }\n\n if (!isRecordingStarted) {\n throw new Error('Recording not started in time.');\n }\n\n return { recordingSpawn, outputPath };\n }\n\n export async function stopScreenRecordingAsync({\n recordingSpawn,\n }: {\n recordingSpawn: SpawnPromise<SpawnResult>;\n }): Promise<void> {\n recordingSpawn.child.kill(2);\n await recordingSpawn;\n }\n}\n\nfunction parseUdidFromBootstatusStdout(stdout: string): IosSimulatorUuid | null {\n const matches = stdout.match(/^Monitoring boot status for .+ \\((.+)\\)\\.$/m);\n if (!matches) {\n return null;\n }\n return matches[1] as IosSimulatorUuid;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"IosSimulatorUtils.js","sourceRoot":"","sources":["../../src/utils/IosSimulatorUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport os from 'node:os';\nimport fs from 'node:fs';\nimport { setTimeout } from 'node:timers/promises';\n\nimport spawn, { SpawnPromise, SpawnResult } from '@expo/turtle-spawn';\nimport { z } from 'zod';\n\nimport { retryAsync } from './retry';\n\nexport type IosSimulatorUuid = string & z.BRAND<'IosSimulatorUuid'>;\nexport type IosSimulatorName = string & z.BRAND<'IosSimulatorName'>;\n\nexport namespace IosSimulatorUtils {\n type XcrunSimctlDevice = {\n availabilityError?: string;\n /** e.g. /Users/sjchmiela/Library/Developer/CoreSimulator/Devices/8272DEB1-42B5-4F78-AB2D-0BC5F320B822/data */\n dataPath: string;\n /** e.g. 18341888 */\n dataPathSize: number;\n /** e.g. /Users/sjchmiela/Library/Logs/CoreSimulator/8272DEB1-42B5-4F78-AB2D-0BC5F320B822 */\n logPath: string;\n /** e.g. 8272DEB1-42B5-4F78-AB2D-0BC5F320B822 */\n udid: IosSimulatorUuid;\n isAvailable: boolean;\n /** e.g. com.apple.CoreSimulator.SimDeviceType.iPhone-13-mini */\n deviceTypeIdentifier: string;\n state: 'Shutdown' | 'Booted';\n /** e.g. iPhone 15 */\n name: IosSimulatorName;\n /** e.g. 2024-01-22T19:28:56Z */\n lastBootedAt?: string;\n };\n\n type SimulatorDevice = XcrunSimctlDevice & { runtime: string; displayName: string };\n\n type XcrunSimctlListDevicesJsonOutput = {\n devices: {\n [runtime: string]: XcrunSimctlDevice[];\n };\n };\n\n export async function getAvailableDevicesAsync({\n env,\n filter,\n }: {\n env: NodeJS.ProcessEnv;\n filter: 'available' | 'booted';\n }): Promise<SimulatorDevice[]> {\n const result = await spawn(\n 'xcrun',\n ['simctl', 'list', 'devices', '--json', '--no-escape-slashes', filter],\n { env }\n );\n const xcrunData = JSON.parse(result.stdout) as XcrunSimctlListDevicesJsonOutput;\n\n const allAvailableDevices: SimulatorDevice[] = [];\n for (const [runtime, devices] of Object.entries(xcrunData.devices)) {\n allAvailableDevices.push(\n ...devices.map((device) => ({\n ...device,\n runtime,\n displayName: `${device.name} (${device.udid}) on ${runtime}`,\n }))\n );\n }\n\n return allAvailableDevices;\n }\n\n export async function getDeviceAsync({\n udid,\n env,\n }: {\n env: NodeJS.ProcessEnv;\n udid: IosSimulatorUuid;\n }): Promise<SimulatorDevice | null> {\n const devices = await getAvailableDevicesAsync({ env, filter: 'available' });\n return devices.find((device) => device.udid === udid) ?? null;\n }\n\n export async function cloneAsync({\n sourceDeviceIdentifier,\n destinationDeviceName,\n env,\n }: {\n sourceDeviceIdentifier: IosSimulatorName | IosSimulatorUuid;\n destinationDeviceName: IosSimulatorName;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n await spawn('xcrun', ['simctl', 'clone', sourceDeviceIdentifier, destinationDeviceName], {\n env,\n });\n }\n\n export async function startAsync({\n deviceIdentifier,\n env,\n }: {\n deviceIdentifier: IosSimulatorUuid | IosSimulatorName;\n env: NodeJS.ProcessEnv;\n }): Promise<{ udid: IosSimulatorUuid }> {\n const bootstatusResult = await spawn(\n 'xcrun',\n ['simctl', 'bootstatus', deviceIdentifier, '-b'],\n {\n env,\n }\n );\n\n const udid = parseUdidFromBootstatusStdout(bootstatusResult.stdout);\n if (!udid) {\n throw new Error('Failed to parse UDID from bootstatus result.');\n }\n\n return { udid };\n }\n\n export async function waitForReadyAsync({\n udid,\n env,\n }: {\n udid: IosSimulatorUuid;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n await retryAsync(\n async () => {\n await spawn('xcrun', ['simctl', 'io', udid, 'screenshot', '/dev/null'], {\n env,\n });\n },\n {\n retryOptions: {\n // There's 30 * 60 seconds in 30 minutes, which is the timeout.\n retries: 30 * 60,\n retryIntervalMs: 1_000,\n },\n }\n );\n }\n\n export async function collectLogsAsync({\n deviceIdentifier,\n env,\n }: {\n deviceIdentifier: IosSimulatorName | IosSimulatorUuid;\n env: NodeJS.ProcessEnv;\n }): Promise<{ outputPath: string }> {\n const outputDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'ios-simulator-logs-'));\n const outputPath = path.join(outputDir, `${deviceIdentifier}.logarchive`);\n\n await spawn(\n 'xcrun',\n ['simctl', 'spawn', deviceIdentifier, 'log', 'collect', '--output', outputPath],\n {\n env,\n }\n );\n\n return { outputPath };\n }\n\n export async function deleteAsync({\n deviceIdentifier,\n env,\n }: {\n deviceIdentifier: IosSimulatorName | IosSimulatorUuid;\n env: NodeJS.ProcessEnv;\n }): Promise<void> {\n await spawn('xcrun', ['simctl', 'shutdown', deviceIdentifier], { env });\n await spawn('xcrun', ['simctl', 'delete', deviceIdentifier], { env });\n }\n\n export async function startScreenRecordingAsync({\n deviceIdentifier,\n env,\n }: {\n deviceIdentifier: IosSimulatorUuid | IosSimulatorName;\n env: NodeJS.ProcessEnv;\n }): Promise<{\n recordingSpawn: SpawnPromise<SpawnResult>;\n outputPath: string;\n }> {\n const outputDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'ios-screen-recording-'));\n const outputPath = path.join(outputDir, `${deviceIdentifier}.mov`);\n const recordingSpawn = spawn(\n 'xcrun',\n ['simctl', 'io', deviceIdentifier, 'recordVideo', '-f', outputPath],\n {\n env,\n }\n );\n\n const stdout = recordingSpawn.child.stdout;\n const stderr = recordingSpawn.child.stderr;\n if (!stdout || !stderr) {\n // No stdout/stderr means the process failed to start, so awaiting it will throw an error.\n await recordingSpawn;\n throw new Error('Recording process failed to start.');\n }\n\n let outputAggregated = '';\n\n // Listen to both stdout and stderr since \"Recording started\" might come from either\n stdout.on('data', (data) => {\n const output = data.toString();\n outputAggregated += output;\n });\n\n stderr.on('data', (data) => {\n const output = data.toString();\n outputAggregated += output;\n });\n\n let isRecordingStarted = false;\n for (let i = 0; i < 20; i++) {\n // Check if recording started message appears in either stdout or stderr\n if (outputAggregated.includes('Recording started')) {\n isRecordingStarted = true;\n break;\n }\n await setTimeout(1000);\n }\n\n if (!isRecordingStarted) {\n throw new Error('Recording not started in time.');\n }\n\n return { recordingSpawn, outputPath };\n }\n\n export async function stopScreenRecordingAsync({\n recordingSpawn,\n }: {\n recordingSpawn: SpawnPromise<SpawnResult>;\n }): Promise<void> {\n recordingSpawn.child.kill(2);\n await recordingSpawn;\n }\n}\n\nfunction parseUdidFromBootstatusStdout(stdout: string): IosSimulatorUuid | null {\n const matches = stdout.match(/^Monitoring boot status for .+ \\((.+)\\)\\.$/m);\n if (!matches) {\n return null;\n }\n return matches[1] as IosSimulatorUuid;\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="bunyan" />
|
|
2
|
+
import { bunyan } from '@expo/logger';
|
|
3
|
+
export declare function findMaestroPathsFlowsToExecuteAsync({ workingDirectory, flowPath, includeTags, excludeTags, logger, }: {
|
|
4
|
+
workingDirectory: string;
|
|
5
|
+
flowPath: string;
|
|
6
|
+
includeTags: string[] | undefined;
|
|
7
|
+
excludeTags: string[] | undefined;
|
|
8
|
+
logger: bunyan;
|
|
9
|
+
}): Promise<string[]>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.findMaestroPathsFlowsToExecuteAsync = void 0;
|
|
27
|
+
const node_fs_1 = require("node:fs");
|
|
28
|
+
const path = __importStar(require("node:path"));
|
|
29
|
+
const yaml = __importStar(require("yaml"));
|
|
30
|
+
const zod_1 = require("zod");
|
|
31
|
+
const results_1 = require("@expo/results");
|
|
32
|
+
const FlowConfigSchema = zod_1.z.object({
|
|
33
|
+
name: zod_1.z.string().optional(),
|
|
34
|
+
tags: zod_1.z.array(zod_1.z.string()).optional(),
|
|
35
|
+
});
|
|
36
|
+
async function findMaestroPathsFlowsToExecuteAsync({ workingDirectory, flowPath, includeTags, excludeTags, logger, }) {
|
|
37
|
+
var _a;
|
|
38
|
+
const absoluteFlowPath = path.resolve(workingDirectory, flowPath);
|
|
39
|
+
// If it's a file, just return it (no validation needed)
|
|
40
|
+
const stat = await node_fs_1.promises.stat(absoluteFlowPath);
|
|
41
|
+
if (stat.isFile()) {
|
|
42
|
+
logger.info(`Found a file: ${path.relative(workingDirectory, absoluteFlowPath)}`);
|
|
43
|
+
return [absoluteFlowPath];
|
|
44
|
+
}
|
|
45
|
+
// It's a directory - discover flow files
|
|
46
|
+
logger.info(`Found a directory: ${path.relative(workingDirectory, absoluteFlowPath)}`);
|
|
47
|
+
logger.info(`Searching for flow files...`);
|
|
48
|
+
const { flows } = await findAndParseFlowFilesAsync({
|
|
49
|
+
dirPath: absoluteFlowPath,
|
|
50
|
+
workingDirectory,
|
|
51
|
+
logger,
|
|
52
|
+
});
|
|
53
|
+
if (flows.length === 0) {
|
|
54
|
+
logger.info(`No valid flow files found in: ${path.relative(workingDirectory, absoluteFlowPath)}`);
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
if (!includeTags && !excludeTags) {
|
|
58
|
+
logger.info(`No tags provided, returning all flows.`);
|
|
59
|
+
return flows.map(({ path }) => path);
|
|
60
|
+
}
|
|
61
|
+
logger.info(`Filtering flows by tags. Tags to include: ${JSON.stringify(includeTags)}. Tags to exclude: ${(_a = JSON.stringify(excludeTags)) !== null && _a !== void 0 ? _a : 'none'}.`);
|
|
62
|
+
return flows
|
|
63
|
+
.filter(({ config, path: flowPath }) => {
|
|
64
|
+
var _a;
|
|
65
|
+
const shouldInclude = matchesTags({
|
|
66
|
+
flowTags: (_a = config === null || config === void 0 ? void 0 : config.tags) !== null && _a !== void 0 ? _a : [],
|
|
67
|
+
includeTags: includeTags !== null && includeTags !== void 0 ? includeTags : [],
|
|
68
|
+
excludeTags: excludeTags !== null && excludeTags !== void 0 ? excludeTags : [],
|
|
69
|
+
});
|
|
70
|
+
logger.info(shouldInclude
|
|
71
|
+
? `- ${path.relative(workingDirectory, flowPath)} matches tags, including.`
|
|
72
|
+
: `- ${path.relative(workingDirectory, flowPath)} does not match tags, excluding.`);
|
|
73
|
+
return shouldInclude;
|
|
74
|
+
})
|
|
75
|
+
.map(({ path }) => path);
|
|
76
|
+
}
|
|
77
|
+
exports.findMaestroPathsFlowsToExecuteAsync = findMaestroPathsFlowsToExecuteAsync;
|
|
78
|
+
async function findAndParseFlowFilesAsync({ workingDirectory, dirPath, logger, }) {
|
|
79
|
+
const flows = [];
|
|
80
|
+
const entries = await node_fs_1.promises.readdir(dirPath, { withFileTypes: true });
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
83
|
+
if (entry.isFile()) {
|
|
84
|
+
// Skip non-YAML files
|
|
85
|
+
const ext = path.extname(fullPath);
|
|
86
|
+
if (ext !== '.yaml' && ext !== '.yml') {
|
|
87
|
+
logger.info(`Skipping non-YAML file: ${path.relative(workingDirectory, fullPath)}`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
// Skip Maestro config files
|
|
91
|
+
const basename = path.basename(fullPath, ext);
|
|
92
|
+
if (basename === 'config') {
|
|
93
|
+
logger.info(`Maestro config files are not supported yet. Skipping Maestro config file: ${path.relative(workingDirectory, fullPath)}`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const result = await (0, results_1.asyncResult)(parseFlowFile(fullPath));
|
|
97
|
+
if (result.ok) {
|
|
98
|
+
logger.info(`Found flow file: ${path.relative(workingDirectory, fullPath)}`);
|
|
99
|
+
flows.push({ config: result.value, path: fullPath });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
logger.info({ err: result.reason }, `Skipping flow file: ${path.relative(workingDirectory, fullPath)}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (entry.isDirectory()) {
|
|
106
|
+
logger.info(`Default behavior excludes subdirectories. Skipping subdirectory: ${path.relative(workingDirectory, fullPath)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { flows };
|
|
110
|
+
}
|
|
111
|
+
async function parseFlowFile(filePath) {
|
|
112
|
+
const content = await node_fs_1.promises.readFile(filePath, 'utf-8');
|
|
113
|
+
const documents = yaml.parseAllDocuments(content);
|
|
114
|
+
const configDoc = documents[0];
|
|
115
|
+
if (!configDoc) {
|
|
116
|
+
throw new Error(`No config section found in ${filePath}`);
|
|
117
|
+
}
|
|
118
|
+
return FlowConfigSchema.parse(configDoc.toJS());
|
|
119
|
+
}
|
|
120
|
+
function matchesTags({ flowTags, includeTags, excludeTags, }) {
|
|
121
|
+
// Include logic: if includeTags is empty OR flow has any of the include tags
|
|
122
|
+
const includeMatch = includeTags.length === 0 || includeTags.some((tag) => flowTags.includes(tag));
|
|
123
|
+
// Exclude logic: if excludeTags is empty OR flow has none of the exclude tags
|
|
124
|
+
const excludeMatch = excludeTags.length === 0 || !excludeTags.some((tag) => flowTags.includes(tag));
|
|
125
|
+
return includeMatch && excludeMatch;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=findMaestroPathsFlowsToExecuteAsync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findMaestroPathsFlowsToExecuteAsync.js","sourceRoot":"","sources":["../../src/utils/findMaestroPathsFlowsToExecuteAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\n\nimport { bunyan } from '@expo/logger';\nimport * as yaml from 'yaml';\nimport { z } from 'zod';\nimport { asyncResult } from '@expo/results';\n\nconst FlowConfigSchema = z.object({\n name: z.string().optional(),\n tags: z.array(z.string()).optional(),\n});\n\ntype FlowConfig = z.infer<typeof FlowConfigSchema>;\n\nexport async function findMaestroPathsFlowsToExecuteAsync({\n workingDirectory,\n flowPath,\n includeTags,\n excludeTags,\n logger,\n}: {\n workingDirectory: string;\n flowPath: string;\n includeTags: string[] | undefined;\n excludeTags: string[] | undefined;\n logger: bunyan;\n}): Promise<string[]> {\n const absoluteFlowPath = path.resolve(workingDirectory, flowPath);\n // If it's a file, just return it (no validation needed)\n const stat = await fs.stat(absoluteFlowPath);\n\n if (stat.isFile()) {\n logger.info(`Found a file: ${path.relative(workingDirectory, absoluteFlowPath)}`);\n return [absoluteFlowPath];\n }\n\n // It's a directory - discover flow files\n logger.info(`Found a directory: ${path.relative(workingDirectory, absoluteFlowPath)}`);\n logger.info(`Searching for flow files...`);\n const { flows } = await findAndParseFlowFilesAsync({\n dirPath: absoluteFlowPath,\n workingDirectory,\n logger,\n });\n\n if (flows.length === 0) {\n logger.info(\n `No valid flow files found in: ${path.relative(workingDirectory, absoluteFlowPath)}`\n );\n return [];\n }\n\n if (!includeTags && !excludeTags) {\n logger.info(`No tags provided, returning all flows.`);\n return flows.map(({ path }) => path);\n }\n\n logger.info(\n `Filtering flows by tags. Tags to include: ${JSON.stringify(includeTags)}. Tags to exclude: ${JSON.stringify(excludeTags) ?? 'none'}.`\n );\n return flows\n .filter(({ config, path: flowPath }) => {\n const shouldInclude = matchesTags({\n flowTags: config?.tags ?? [],\n includeTags: includeTags ?? [],\n excludeTags: excludeTags ?? [],\n });\n\n logger.info(\n shouldInclude\n ? `- ${path.relative(workingDirectory, flowPath)} matches tags, including.`\n : `- ${path.relative(workingDirectory, flowPath)} does not match tags, excluding.`\n );\n\n return shouldInclude;\n })\n .map(({ path }) => path);\n}\n\nasync function findAndParseFlowFilesAsync({\n workingDirectory,\n dirPath,\n logger,\n}: {\n workingDirectory: string;\n dirPath: string;\n logger: bunyan;\n}): Promise<{ flows: { config: FlowConfig; path: string }[] }> {\n const flows: { config: FlowConfig; path: string }[] = [];\n\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isFile()) {\n // Skip non-YAML files\n const ext = path.extname(fullPath);\n if (ext !== '.yaml' && ext !== '.yml') {\n logger.info(`Skipping non-YAML file: ${path.relative(workingDirectory, fullPath)}`);\n continue;\n }\n\n // Skip Maestro config files\n const basename = path.basename(fullPath, ext);\n if (basename === 'config') {\n logger.info(\n `Maestro config files are not supported yet. Skipping Maestro config file: ${path.relative(workingDirectory, fullPath)}`\n );\n continue;\n }\n\n const result = await asyncResult(parseFlowFile(fullPath));\n if (result.ok) {\n logger.info(`Found flow file: ${path.relative(workingDirectory, fullPath)}`);\n flows.push({ config: result.value, path: fullPath });\n } else {\n logger.info(\n { err: result.reason },\n `Skipping flow file: ${path.relative(workingDirectory, fullPath)}`\n );\n }\n } else if (entry.isDirectory()) {\n logger.info(\n `Default behavior excludes subdirectories. Skipping subdirectory: ${path.relative(workingDirectory, fullPath)}`\n );\n }\n }\n\n return { flows };\n}\n\nasync function parseFlowFile(filePath: string): Promise<FlowConfig> {\n const content = await fs.readFile(filePath, 'utf-8');\n const documents = yaml.parseAllDocuments(content);\n const configDoc = documents[0];\n if (!configDoc) {\n throw new Error(`No config section found in ${filePath}`);\n }\n return FlowConfigSchema.parse(configDoc.toJS());\n}\n\nfunction matchesTags({\n flowTags,\n includeTags,\n excludeTags,\n}: {\n flowTags: string[];\n includeTags: string[];\n excludeTags: string[];\n}): boolean {\n // Include logic: if includeTags is empty OR flow has any of the include tags\n const includeMatch =\n includeTags.length === 0 || includeTags.some((tag) => flowTags.includes(tag));\n\n // Exclude logic: if excludeTags is empty OR flow has none of the exclude tags\n const excludeMatch =\n excludeTags.length === 0 || !excludeTags.some((tag) => flowTags.includes(tag));\n\n return includeMatch && excludeMatch;\n}\n"]}
|
package/dist/utils/strings.d.ts
CHANGED
package/dist/utils/strings.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.pluralize = void 0;
|
|
3
|
+
exports.PlatformToProperNounMap = exports.pluralize = void 0;
|
|
4
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
4
5
|
const PLURAL_WORDS = {
|
|
5
6
|
entry: 'entries',
|
|
6
7
|
};
|
|
@@ -11,4 +12,8 @@ const pluralize = (count, word) => {
|
|
|
11
12
|
return shouldUsePluralWord ? pluralWord : word;
|
|
12
13
|
};
|
|
13
14
|
exports.pluralize = pluralize;
|
|
15
|
+
exports.PlatformToProperNounMap = {
|
|
16
|
+
[eas_build_job_1.Platform.ANDROID]: 'Android',
|
|
17
|
+
[eas_build_job_1.Platform.IOS]: 'iOS',
|
|
18
|
+
};
|
|
14
19
|
//# sourceMappingURL=strings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strings.js","sourceRoot":"","sources":["../../src/utils/strings.ts"],"names":[],"mappings":";;;AAAA,MAAM,YAAY,GAA2B;IAC3C,KAAK,EAAE,SAAS;CACjB,CAAC;AAEK,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,IAAY,EAAU,EAAE;;IAC/D,MAAM,mBAAmB,GAAG,KAAK,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,MAAA,YAAY,CAAC,IAAI,CAAC,mCAAI,GAAG,IAAI,GAAG,CAAC;IAEpD,OAAO,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC,CAAC;AALW,QAAA,SAAS,aAKpB","sourcesContent":["
|
|
1
|
+
{"version":3,"file":"strings.js","sourceRoot":"","sources":["../../src/utils/strings.ts"],"names":[],"mappings":";;;AAAA,uDAA+C;AAE/C,MAAM,YAAY,GAA2B;IAC3C,KAAK,EAAE,SAAS;CACjB,CAAC;AAEK,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,IAAY,EAAU,EAAE;;IAC/D,MAAM,mBAAmB,GAAG,KAAK,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,MAAA,YAAY,CAAC,IAAI,CAAC,mCAAI,GAAG,IAAI,GAAG,CAAC;IAEpD,OAAO,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC,CAAC;AALW,QAAA,SAAS,aAKpB;AAEW,QAAA,uBAAuB,GAA6B;IAC/D,CAAC,wBAAQ,CAAC,OAAO,CAAC,EAAE,SAAS;IAC7B,CAAC,wBAAQ,CAAC,GAAG,CAAC,EAAE,KAAK;CACtB,CAAC","sourcesContent":["import { Platform } from '@expo/eas-build-job';\n\nconst PLURAL_WORDS: Record<string, string> = {\n entry: 'entries',\n};\n\nexport const pluralize = (count: number, word: string): string => {\n const shouldUsePluralWord = count > 1 || count === 0;\n const pluralWord = PLURAL_WORDS[word] ?? `${word}s`;\n\n return shouldUsePluralWord ? pluralWord : word;\n};\n\nexport const PlatformToProperNounMap: Record<Platform, string> = {\n [Platform.ANDROID]: 'Android',\n [Platform.IOS]: 'iOS',\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/build-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.199",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"resolve-from": "^5.0.0",
|
|
52
52
|
"retry": "^0.13.1",
|
|
53
53
|
"semver": "^7.6.2",
|
|
54
|
-
"tar": "^7.4.3"
|
|
54
|
+
"tar": "^7.4.3",
|
|
55
|
+
"yaml": "^2.8.1"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@expo/repack-app": "~0.2.5",
|
|
@@ -78,5 +79,5 @@
|
|
|
78
79
|
"node": "20.14.0",
|
|
79
80
|
"yarn": "1.22.21"
|
|
80
81
|
},
|
|
81
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "ce5b578c8a33028cd68b6f29872ea6dfdbf0a4fe"
|
|
82
83
|
}
|