@expo/build-tools 20.4.0 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/android/gradle.d.ts +5 -0
- package/dist/android/gradle.js +13 -33
- package/dist/common/installDependencies.js +21 -18
- package/dist/common/projectSources.js +1 -1
- package/dist/context.d.ts +2 -2
- package/dist/context.js +31 -1
- package/dist/ios/fastlane.d.ts +1 -1
- package/dist/steps/easFunctions.js +18 -0
- package/dist/steps/functions/capturePosthogEvent.d.ts +2 -0
- package/dist/steps/functions/capturePosthogEvent.js +79 -0
- package/dist/steps/functions/createPosthogAnnotation.d.ts +2 -0
- package/dist/steps/functions/createPosthogAnnotation.js +75 -0
- package/dist/steps/functions/createSubmissionEntity.js +25 -1
- package/dist/steps/functions/downloadArtifact.js +13 -3
- package/dist/steps/functions/finishIosSimulatorRecordings.d.ts +2 -0
- package/dist/steps/functions/finishIosSimulatorRecordings.js +24 -0
- package/dist/steps/functions/repack.js +12 -7
- package/dist/steps/functions/restoreCache.js +2 -1
- package/dist/steps/functions/rolloutPosthogFlag.d.ts +2 -0
- package/dist/steps/functions/rolloutPosthogFlag.js +208 -0
- package/dist/steps/functions/saveCache.js +3 -2
- package/dist/steps/functions/startAgentDeviceRemoteSession.js +21 -8
- package/dist/steps/functions/startArgentRemoteSession.d.ts +14 -0
- package/dist/steps/functions/startArgentRemoteSession.js +105 -44
- package/dist/steps/functions/startIosSimulator.js +19 -0
- package/dist/steps/functions/startIosSimulatorRecordings.d.ts +2 -0
- package/dist/steps/functions/startIosSimulatorRecordings.js +20 -0
- package/dist/steps/functions/startServeSimRemoteSession.js +7 -5
- package/dist/steps/functions/uploadDeviceRunSessionScreenRecordings.d.ts +3 -0
- package/dist/steps/functions/uploadDeviceRunSessionScreenRecordings.js +91 -0
- package/dist/steps/functions/uploadPosthogSourcemaps.d.ts +2 -0
- package/dist/steps/functions/uploadPosthogSourcemaps.js +83 -0
- package/dist/steps/functions/waitForPosthogMetric.d.ts +2 -0
- package/dist/steps/functions/waitForPosthogMetric.js +135 -0
- package/dist/steps/functions/waitForPosthogQuery.d.ts +2 -0
- package/dist/steps/functions/waitForPosthogQuery.js +95 -0
- package/dist/steps/utils/IosSimulatorRecordingUtils.d.ts +17 -0
- package/dist/steps/utils/IosSimulatorRecordingUtils.js +218 -0
- package/dist/steps/utils/PosthogClient.d.ts +46 -0
- package/dist/steps/utils/PosthogClient.js +156 -0
- package/dist/steps/utils/PosthogUtils.d.ts +41 -0
- package/dist/steps/utils/PosthogUtils.js +65 -0
- package/dist/steps/utils/agentDeviceArtifacts.d.ts +27 -0
- package/dist/steps/utils/agentDeviceArtifacts.js +133 -0
- package/dist/steps/utils/android/gradle.d.ts +5 -0
- package/dist/steps/utils/android/gradle.js +13 -62
- package/dist/steps/utils/argentArtifacts.d.ts +5 -3
- package/dist/steps/utils/argentArtifacts.js +95 -23
- package/dist/steps/utils/deviceRunSessionArtifacts.d.ts +3 -1
- package/dist/steps/utils/deviceRunSessionArtifacts.js +6 -2
- package/dist/steps/utils/remoteDeviceRunSession.d.ts +8 -0
- package/dist/steps/utils/remoteDeviceRunSession.js +62 -1
- package/dist/utils/AndroidEmulatorUtils.js +3 -0
- package/dist/utils/IosSimulatorUtils.d.ts +5 -0
- package/dist/utils/IosSimulatorUtils.js +81 -2
- package/dist/utils/expoUpdates.js +6 -1
- package/dist/utils/expoUpdatesEmbedded.js +4 -0
- package/dist/utils/processes.d.ts +1 -0
- package/dist/utils/processes.js +23 -0
- package/package.json +9 -8
- package/resources/record-sim/Package.swift +28 -0
- package/resources/record-sim/README.md +79 -0
- package/resources/record-sim/Sources/RecordSim/FramebufferDisplaySource.swift +192 -0
- package/resources/record-sim/Sources/RecordSim/PrivateSimulatorServices.swift +96 -0
- package/resources/record-sim/Sources/RecordSim/RecordingModels.swift +70 -0
- package/resources/record-sim/Sources/RecordSim/RecordingOutputWriter.swift +136 -0
- package/resources/record-sim/Sources/RecordSim/SimulatorRecorder.swift +632 -0
- package/resources/record-sim/Sources/RecordSim/Utilities.swift +60 -0
- package/resources/record-sim/Sources/record-sim/main.swift +143 -0
|
@@ -13,12 +13,14 @@ const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
|
13
13
|
const node_os_1 = __importDefault(require("node:os"));
|
|
14
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
15
|
const promises_2 = require("node:stream/promises");
|
|
16
|
+
const promises_3 = require("node:timers/promises");
|
|
16
17
|
const zod_1 = require("zod");
|
|
17
18
|
const sentry_1 = require("../../sentry");
|
|
18
19
|
const artifacts_1 = require("../../utils/artifacts");
|
|
19
|
-
const retry_1 = require("../../utils/retry");
|
|
20
20
|
const deviceRunSessionArtifacts_1 = require("./deviceRunSessionArtifacts");
|
|
21
21
|
const ARGENT_ARTIFACT_UPLOAD_POLL_INTERVAL_MS = 5_000;
|
|
22
|
+
const ARGENT_ARTIFACT_UPLOAD_CLEANUP_TIMEOUT_MS = 30_000;
|
|
23
|
+
const ARGENT_ARTIFACT_FETCH_TIMEOUT_MS = 10_000;
|
|
22
24
|
const ArgentArtifactSchema = zod_1.z.object({
|
|
23
25
|
id: zod_1.z.string(),
|
|
24
26
|
filename: zod_1.z.string(),
|
|
@@ -28,46 +30,73 @@ const ArgentArtifactSchema = zod_1.z.object({
|
|
|
28
30
|
const ArgentArtifactsListResponseSchema = zod_1.z.object({
|
|
29
31
|
artifacts: zod_1.z.array(ArgentArtifactSchema),
|
|
30
32
|
});
|
|
31
|
-
async function pollArgentArtifactsForUploadAsync(ctx, { deviceRunSessionId, toolsUrl, toolsAuthToken, logger, }) {
|
|
33
|
+
async function pollArgentArtifactsForUploadAsync(ctx, { deviceRunSessionId, toolsUrl, toolsAuthToken, logger, signal, }) {
|
|
32
34
|
logger.info('Started polling Argent tool-server for artifacts.');
|
|
33
35
|
const seenArtifactIds = new Set();
|
|
36
|
+
const pendingUploads = new Set();
|
|
34
37
|
let listArtifactsErrorCount = 0;
|
|
35
|
-
|
|
38
|
+
const queueArtifactUpload = (artifact) => {
|
|
39
|
+
if (seenArtifactIds.has(artifact.id)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
seenArtifactIds.add(artifact.id);
|
|
43
|
+
const uploadPromise = uploadArgentArtifactAsync(ctx, {
|
|
44
|
+
deviceRunSessionId,
|
|
45
|
+
toolsUrl,
|
|
46
|
+
toolsAuthToken,
|
|
47
|
+
artifact,
|
|
48
|
+
logger,
|
|
49
|
+
}).catch(err => {
|
|
50
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
51
|
+
sentry_1.Sentry.capture('Could not upload Argent remote session artifact', error);
|
|
52
|
+
logger.warn({ err: error }, 'Could not upload Argent remote session artifact.');
|
|
53
|
+
});
|
|
54
|
+
pendingUploads.add(uploadPromise);
|
|
55
|
+
void uploadPromise.finally(() => {
|
|
56
|
+
pendingUploads.delete(uploadPromise);
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const listArtifactsForUploadAsync = async ({ signal, } = {}) => {
|
|
36
60
|
try {
|
|
37
|
-
const artifacts = await listArgentArtifactsAsync({ toolsUrl, toolsAuthToken });
|
|
61
|
+
const artifacts = await listArgentArtifactsAsync({ toolsUrl, toolsAuthToken, signal });
|
|
38
62
|
listArtifactsErrorCount = 0;
|
|
39
|
-
|
|
40
|
-
if (seenArtifactIds.has(artifact.id)) {
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
seenArtifactIds.add(artifact.id);
|
|
44
|
-
void uploadArgentArtifactAsync(ctx, {
|
|
45
|
-
deviceRunSessionId,
|
|
46
|
-
toolsUrl,
|
|
47
|
-
toolsAuthToken,
|
|
48
|
-
artifact,
|
|
49
|
-
logger,
|
|
50
|
-
}).catch(err => {
|
|
51
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
52
|
-
sentry_1.Sentry.capture('Could not upload Argent remote session artifact', error);
|
|
53
|
-
logger.warn({ err: error }, 'Could not upload Argent remote session artifact.');
|
|
54
|
-
});
|
|
55
|
-
}
|
|
63
|
+
return artifacts;
|
|
56
64
|
}
|
|
57
65
|
catch (err) {
|
|
66
|
+
if (signal?.aborted) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
58
69
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
59
70
|
listArtifactsErrorCount += 1;
|
|
60
71
|
if (listArtifactsErrorCount === 1 || listArtifactsErrorCount % 5 === 0) {
|
|
61
72
|
sentry_1.Sentry.capture('Could not list Argent remote session artifacts', error);
|
|
62
73
|
logger.warn({ err: error, failedArtifactListCount: listArtifactsErrorCount }, 'Could not list Argent remote session artifacts.');
|
|
63
74
|
}
|
|
75
|
+
return [];
|
|
64
76
|
}
|
|
65
|
-
|
|
77
|
+
};
|
|
78
|
+
const listAndQueueArtifactUploadsAsync = async ({ signal, } = {}) => {
|
|
79
|
+
const artifacts = await listArtifactsForUploadAsync({ signal });
|
|
80
|
+
for (const artifact of artifacts) {
|
|
81
|
+
queueArtifactUpload(artifact);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
while (!signal.aborted) {
|
|
85
|
+
await listAndQueueArtifactUploadsAsync({ signal });
|
|
86
|
+
await sleepUntilAbortedAsync(ARGENT_ARTIFACT_UPLOAD_POLL_INTERVAL_MS, signal);
|
|
66
87
|
}
|
|
88
|
+
logger.info('Argent artifact polling stopped; draining remaining artifacts.');
|
|
89
|
+
await listAndQueueArtifactUploadsAsync();
|
|
90
|
+
await waitForPendingUploadsAsync({
|
|
91
|
+
pendingUploads,
|
|
92
|
+
timeoutMs: ARGENT_ARTIFACT_UPLOAD_CLEANUP_TIMEOUT_MS,
|
|
93
|
+
logger,
|
|
94
|
+
});
|
|
67
95
|
}
|
|
68
|
-
async function listArgentArtifactsAsync({ toolsUrl, toolsAuthToken, }) {
|
|
96
|
+
async function listArgentArtifactsAsync({ toolsUrl, toolsAuthToken, signal, }) {
|
|
69
97
|
const response = await (0, node_fetch_1.default)(new URL('/artifacts', toolsUrl).toString(), {
|
|
70
98
|
headers: toolsAuthToken ? { Authorization: `Bearer ${toolsAuthToken}` } : {},
|
|
99
|
+
signal: createTimeoutSignal({ signal, timeoutMs: ARGENT_ARTIFACT_FETCH_TIMEOUT_MS }),
|
|
71
100
|
});
|
|
72
101
|
if (!response.ok) {
|
|
73
102
|
throw new eas_build_job_1.SystemError(`Failed to list Argent artifacts: ${response.status} ${response.statusText}`);
|
|
@@ -97,6 +126,7 @@ async function uploadArgentArtifactAsync(ctx, { deviceRunSessionId, toolsUrl, to
|
|
|
97
126
|
artifactId: artifact.id,
|
|
98
127
|
name: `${filename} (${artifact.id})`,
|
|
99
128
|
filename,
|
|
129
|
+
kind: undefined,
|
|
100
130
|
size,
|
|
101
131
|
stream: (0, node_fs_1.createReadStream)(temporaryArtifactPath),
|
|
102
132
|
});
|
|
@@ -108,6 +138,10 @@ async function uploadArgentArtifactAsync(ctx, { deviceRunSessionId, toolsUrl, to
|
|
|
108
138
|
async function downloadArgentArtifactToFileAsync({ artifact, toolsUrl, toolsAuthToken, destinationPath, }) {
|
|
109
139
|
const response = await (0, node_fetch_1.default)(new URL(`/artifacts/${artifact.id}`, toolsUrl).toString(), {
|
|
110
140
|
headers: toolsAuthToken ? { Authorization: `Bearer ${toolsAuthToken}` } : {},
|
|
141
|
+
signal: createTimeoutSignal({
|
|
142
|
+
signal: undefined,
|
|
143
|
+
timeoutMs: ARGENT_ARTIFACT_FETCH_TIMEOUT_MS,
|
|
144
|
+
}),
|
|
111
145
|
});
|
|
112
146
|
if (!response.ok) {
|
|
113
147
|
throw new eas_build_job_1.SystemError(`Failed to download Argent artifact ${artifact.id}: ${response.status} ${response.statusText}`);
|
|
@@ -117,3 +151,41 @@ async function downloadArgentArtifactToFileAsync({ artifact, toolsUrl, toolsAuth
|
|
|
117
151
|
}
|
|
118
152
|
await (0, promises_2.pipeline)(response.body, (0, node_fs_1.createWriteStream)(destinationPath));
|
|
119
153
|
}
|
|
154
|
+
async function sleepUntilAbortedAsync(timeoutMs, signal) {
|
|
155
|
+
try {
|
|
156
|
+
await (0, promises_3.setTimeout)(timeoutMs, undefined, { signal });
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
if (!signal.aborted) {
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function createTimeoutSignal({ signal, timeoutMs, }) {
|
|
165
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
166
|
+
return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
167
|
+
}
|
|
168
|
+
async function waitForPendingUploadsAsync({ pendingUploads, timeoutMs, logger, }) {
|
|
169
|
+
if (pendingUploads.size === 0) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
logger.info(`Waiting for ${pendingUploads.size} Argent artifact upload(s) to finish.`);
|
|
173
|
+
let timeout;
|
|
174
|
+
try {
|
|
175
|
+
await Promise.race([
|
|
176
|
+
Promise.allSettled([...pendingUploads]),
|
|
177
|
+
new Promise((_resolve, reject) => {
|
|
178
|
+
timeout = setTimeout(() => {
|
|
179
|
+
reject(new eas_build_job_1.SystemError(`Timed out after ${Math.round(timeoutMs / 1000)}s waiting for Argent artifact uploads.`, {
|
|
180
|
+
trackingCode: 'SIMULATOR_ARTIFACT_SLOW_UPLOAD',
|
|
181
|
+
}));
|
|
182
|
+
}, timeoutMs);
|
|
183
|
+
}),
|
|
184
|
+
]);
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
if (timeout) {
|
|
188
|
+
clearTimeout(timeout);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { CustomBuildContext } from '../../customBuildContext';
|
|
2
|
-
export declare function uploadDeviceRunSessionArtifactAsync(ctx: CustomBuildContext, { deviceRunSessionId, artifactId, name, filename, size, stream, }: {
|
|
2
|
+
export declare function uploadDeviceRunSessionArtifactAsync(ctx: CustomBuildContext, { deviceRunSessionId, artifactId, name, filename, kind, metadata, size, stream, }: {
|
|
3
3
|
deviceRunSessionId: string;
|
|
4
4
|
artifactId: string;
|
|
5
5
|
name: string;
|
|
6
6
|
filename: string;
|
|
7
|
+
kind: string | undefined;
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
7
9
|
size: number;
|
|
8
10
|
stream: NodeJS.ReadableStream;
|
|
9
11
|
}): Promise<void>;
|
|
@@ -52,12 +52,14 @@ const CREATE_DEVICE_RUN_SESSION_ARTIFACT_UPLOAD_SESSION_MUTATION = (0, gql_tada_
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
`);
|
|
55
|
-
async function uploadDeviceRunSessionArtifactAsync(ctx, { deviceRunSessionId, artifactId, name, filename, size, stream, }) {
|
|
55
|
+
async function uploadDeviceRunSessionArtifactAsync(ctx, { deviceRunSessionId, artifactId, name, filename, kind, metadata, size, stream, }) {
|
|
56
56
|
const uploadSession = await createDeviceRunSessionArtifactUploadSessionAsync(ctx, {
|
|
57
57
|
deviceRunSessionId,
|
|
58
58
|
artifactId,
|
|
59
59
|
name,
|
|
60
60
|
filename,
|
|
61
|
+
kind,
|
|
62
|
+
metadata,
|
|
61
63
|
size,
|
|
62
64
|
});
|
|
63
65
|
const response = await (0, node_fetch_1.default)(uploadSession.url, {
|
|
@@ -69,13 +71,15 @@ async function uploadDeviceRunSessionArtifactAsync(ctx, { deviceRunSessionId, ar
|
|
|
69
71
|
throw new eas_build_job_1.SystemError(`Failed to upload device run session artifact ${artifactId}: ${response.status} ${response.statusText}`, { cause: response });
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
|
-
async function createDeviceRunSessionArtifactUploadSessionAsync(ctx, { deviceRunSessionId, artifactId, name, filename, size, }) {
|
|
74
|
+
async function createDeviceRunSessionArtifactUploadSessionAsync(ctx, { deviceRunSessionId, artifactId, name, filename, kind, metadata, size, }) {
|
|
73
75
|
const result = await ctx.graphqlClient
|
|
74
76
|
.mutation(CREATE_DEVICE_RUN_SESSION_ARTIFACT_UPLOAD_SESSION_MUTATION, {
|
|
75
77
|
deviceRunSessionId,
|
|
76
78
|
input: {
|
|
77
79
|
name,
|
|
78
80
|
filename,
|
|
81
|
+
...(kind !== undefined ? { kind } : {}),
|
|
82
|
+
...(metadata !== undefined ? { metadata } : {}),
|
|
79
83
|
size,
|
|
80
84
|
},
|
|
81
85
|
})
|
|
@@ -15,6 +15,12 @@ export declare function selectXcodeDeveloperDirectoryAsync({ env, logger, }: {
|
|
|
15
15
|
env: BuildStepEnv;
|
|
16
16
|
logger: bunyan;
|
|
17
17
|
}): Promise<void>;
|
|
18
|
+
export declare function waitForDeviceRunSessionStoppedAsync({ ctx, deviceRunSessionId, logger, signal, }: {
|
|
19
|
+
ctx: CustomBuildContext;
|
|
20
|
+
deviceRunSessionId: string;
|
|
21
|
+
logger: bunyan;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
}): Promise<void>;
|
|
18
24
|
/**
|
|
19
25
|
* Translate Cloudflare ICE servers into serve-sim CLI flags: `--stun-url` (the
|
|
20
26
|
* credential-less entries) and `--turn-url`/`--turn-username`/`--turn-credential`
|
|
@@ -42,6 +48,8 @@ export declare function uploadRemoteSessionConfigAsync({ ctx, deviceRunSessionId
|
|
|
42
48
|
logger: bunyan;
|
|
43
49
|
}): Promise<void>;
|
|
44
50
|
export type DetachedProcessHandle = {
|
|
51
|
+
/** PID of the directly spawned process, if the OS assigned one. */
|
|
52
|
+
pid: number | undefined;
|
|
45
53
|
getOutput: () => string;
|
|
46
54
|
};
|
|
47
55
|
export declare function spawnDetached({ command, args, cwd, env, }: {
|
|
@@ -40,6 +40,7 @@ exports.getDeviceRunSessionIdOrThrow = getDeviceRunSessionIdOrThrow;
|
|
|
40
40
|
exports.getNgrokTunnelDomainOrThrow = getNgrokTunnelDomainOrThrow;
|
|
41
41
|
exports.getNgrokAuthtokenOrThrow = getNgrokAuthtokenOrThrow;
|
|
42
42
|
exports.selectXcodeDeveloperDirectoryAsync = selectXcodeDeveloperDirectoryAsync;
|
|
43
|
+
exports.waitForDeviceRunSessionStoppedAsync = waitForDeviceRunSessionStoppedAsync;
|
|
43
44
|
exports.turnIceServersToServeSimArgs = turnIceServersToServeSimArgs;
|
|
44
45
|
exports.fetchServeSimTurnArgsAsync = fetchServeSimTurnArgsAsync;
|
|
45
46
|
exports.uploadRemoteSessionConfigAsync = uploadRemoteSessionConfigAsync;
|
|
@@ -56,6 +57,7 @@ const nullthrows_1 = __importDefault(require("nullthrows"));
|
|
|
56
57
|
const zod_1 = require("zod");
|
|
57
58
|
const node_crypto_1 = require("node:crypto");
|
|
58
59
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
60
|
+
const promises_1 = require("node:timers/promises");
|
|
59
61
|
const sentry_1 = require("../../sentry");
|
|
60
62
|
const retry_1 = require("../../utils/retry");
|
|
61
63
|
const turtleFetch_1 = require("../../utils/turtleFetch");
|
|
@@ -73,6 +75,17 @@ const START_DEVICE_RUN_SESSION_MUTATION = (0, gql_tada_1.graphql)(`
|
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
`);
|
|
78
|
+
const DEVICE_RUN_SESSION_STATUS_QUERY = (0, gql_tada_1.graphql)(`
|
|
79
|
+
query DeviceRunSessionStatus($deviceRunSessionId: ID!) {
|
|
80
|
+
deviceRunSessions {
|
|
81
|
+
byId(deviceRunSessionId: $deviceRunSessionId) {
|
|
82
|
+
id
|
|
83
|
+
status
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`);
|
|
88
|
+
const DEVICE_RUN_SESSION_STATUS_POLL_INTERVAL_MS = 5_000;
|
|
76
89
|
function getDeviceRunSessionIdOrThrow(env) {
|
|
77
90
|
const deviceRunSessionId = env.DEVICE_RUN_SESSION_ID;
|
|
78
91
|
if (!deviceRunSessionId) {
|
|
@@ -117,6 +130,54 @@ async function selectXcodeDeveloperDirectoryAsync({ env, logger, }) {
|
|
|
117
130
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
118
131
|
});
|
|
119
132
|
}
|
|
133
|
+
async function waitForDeviceRunSessionStoppedAsync({ ctx, deviceRunSessionId, logger, signal, }) {
|
|
134
|
+
logger.info(`Remote session is live. Polling device run session ${deviceRunSessionId} until it is stopped.`);
|
|
135
|
+
let pollErrorCount = 0;
|
|
136
|
+
while (!signal?.aborted) {
|
|
137
|
+
try {
|
|
138
|
+
const result = await ctx.graphqlClient
|
|
139
|
+
.query(DEVICE_RUN_SESSION_STATUS_QUERY, { deviceRunSessionId })
|
|
140
|
+
.toPromise();
|
|
141
|
+
if (result.error) {
|
|
142
|
+
throw result.error;
|
|
143
|
+
}
|
|
144
|
+
const status = result.data?.deviceRunSessions?.byId?.status;
|
|
145
|
+
if (!status) {
|
|
146
|
+
throw new Error(`Device run session ${deviceRunSessionId} status response was missing.`);
|
|
147
|
+
}
|
|
148
|
+
pollErrorCount = 0;
|
|
149
|
+
if (status === 'STOPPED') {
|
|
150
|
+
logger.info(`Device run session ${deviceRunSessionId} was stopped.`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (status === 'ERRORED') {
|
|
154
|
+
throw new eas_build_job_1.SystemError(`Device run session ${deviceRunSessionId} errored.`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
if (err instanceof eas_build_job_1.SystemError) {
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
162
|
+
pollErrorCount += 1;
|
|
163
|
+
if (pollErrorCount === 1 || pollErrorCount % 5 === 0) {
|
|
164
|
+
sentry_1.Sentry.capture('Could not poll device run session status', error, { level: 'warning' });
|
|
165
|
+
logger.warn({ err: error, failedStatusPollCount: pollErrorCount }, 'Could not poll device run session status; will retry.');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
await sleepUntilAbortedAsync(DEVICE_RUN_SESSION_STATUS_POLL_INTERVAL_MS, signal);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function sleepUntilAbortedAsync(timeoutMs, signal) {
|
|
172
|
+
try {
|
|
173
|
+
await (0, promises_1.setTimeout)(timeoutMs, undefined, signal ? { signal } : undefined);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
if (!signal?.aborted) {
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
120
181
|
const TurnIceServersResponseSchema = zod_1.z.object({
|
|
121
182
|
data: zod_1.z.object({
|
|
122
183
|
iceServers: TurnIceServersSchema,
|
|
@@ -204,7 +265,7 @@ function spawnDetached({ command, args, cwd, env, }) {
|
|
|
204
265
|
};
|
|
205
266
|
promise.child.stdout?.on('data', appendChunk);
|
|
206
267
|
promise.child.stderr?.on('data', appendChunk);
|
|
207
|
-
return { getOutput: () => output };
|
|
268
|
+
return { pid: promise.child.pid, getOutput: () => output };
|
|
208
269
|
}
|
|
209
270
|
async function startServeSimWithTunnelAsync(ctx, { baseDomain, env, logger, timeoutMs, }) {
|
|
210
271
|
logger.info('Launching serve-sim with tunnel.');
|
|
@@ -240,6 +240,9 @@ var AndroidEmulatorUtils;
|
|
|
240
240
|
await emulatorPromise;
|
|
241
241
|
}
|
|
242
242
|
emulatorPromise.child.unref();
|
|
243
|
+
// The emulator is detached and managed through adb. Observe its process promise
|
|
244
|
+
// immediately so expected exits during retry cleanup do not become unhandled rejections.
|
|
245
|
+
void (0, results_1.asyncResult)(emulatorPromise);
|
|
243
246
|
const serialId = await (0, retry_1.retryAsync)(async () => {
|
|
244
247
|
const serialId = await getSerialIdAsync({ deviceName, env });
|
|
245
248
|
(0, assert_1.default)(serialId, `Failed to configure emulator (${serialId}): emulator with required ID not found.`);
|
|
@@ -24,6 +24,7 @@ export declare namespace IosSimulatorUtils {
|
|
|
24
24
|
};
|
|
25
25
|
type SimulatorDevice = XcrunSimctlDevice & {
|
|
26
26
|
runtime: string;
|
|
27
|
+
runtimeDisplayName: string;
|
|
27
28
|
displayName: string;
|
|
28
29
|
};
|
|
29
30
|
export function getAvailableDevicesAsync({ env, filter, }: {
|
|
@@ -39,6 +40,10 @@ export declare namespace IosSimulatorUtils {
|
|
|
39
40
|
destinationDeviceName: IosSimulatorName;
|
|
40
41
|
env: NodeJS.ProcessEnv;
|
|
41
42
|
}): Promise<void>;
|
|
43
|
+
export function enableAccessibilitySettingsAsync({ deviceIdentifier, env, }: {
|
|
44
|
+
deviceIdentifier: IosSimulatorUuid | IosSimulatorName;
|
|
45
|
+
env: NodeJS.ProcessEnv;
|
|
46
|
+
}): Promise<void>;
|
|
42
47
|
export function startAsync({ deviceIdentifier, env, }: {
|
|
43
48
|
deviceIdentifier: IosSimulatorUuid | IosSimulatorName;
|
|
44
49
|
env: NodeJS.ProcessEnv;
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.IosSimulatorUtils = void 0;
|
|
7
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
7
8
|
const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
|
|
8
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
10
|
const node_os_1 = __importDefault(require("node:os"));
|
|
@@ -20,6 +21,7 @@ var IosSimulatorUtils;
|
|
|
20
21
|
allAvailableDevices.push(...devices.map(device => ({
|
|
21
22
|
...device,
|
|
22
23
|
runtime,
|
|
24
|
+
runtimeDisplayName: formatRuntimeDisplayName(runtime),
|
|
23
25
|
displayName: `${device.name} (${device.udid}) on ${runtime}`,
|
|
24
26
|
})));
|
|
25
27
|
}
|
|
@@ -37,6 +39,45 @@ var IosSimulatorUtils;
|
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
IosSimulatorUtils.cloneAsync = cloneAsync;
|
|
42
|
+
async function enableAccessibilitySettingsAsync({ deviceIdentifier, env, }) {
|
|
43
|
+
try {
|
|
44
|
+
const devices = await getAvailableDevicesAsync({ env, filter: 'available' });
|
|
45
|
+
const device = devices.find(device => device.isAvailable &&
|
|
46
|
+
(device.udid === deviceIdentifier || device.name === deviceIdentifier));
|
|
47
|
+
if (!device) {
|
|
48
|
+
throw new eas_build_job_1.UserError('EAS_IOS_SIMULATOR_NOT_FOUND', `Failed to find available iOS Simulator "${deviceIdentifier}" to update accessibility settings.`);
|
|
49
|
+
}
|
|
50
|
+
if (device.state !== 'Shutdown') {
|
|
51
|
+
throw new eas_build_job_1.UserError('EAS_IOS_SIMULATOR_NOT_SHUTDOWN', `Expected iOS Simulator "${deviceIdentifier}" to be shutdown before updating accessibility settings, but it is ${device.state}.`);
|
|
52
|
+
}
|
|
53
|
+
const plistPath = node_path_1.default.join(device.dataPath, 'Library', 'Preferences', 'com.apple.Accessibility.plist');
|
|
54
|
+
await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(plistPath), { recursive: true });
|
|
55
|
+
const plistExists = await node_fs_1.default.promises
|
|
56
|
+
.access(plistPath)
|
|
57
|
+
.then(() => true)
|
|
58
|
+
.catch(() => false);
|
|
59
|
+
if (!plistExists) {
|
|
60
|
+
await (0, turtle_spawn_1.default)('plutil', ['-create', 'binary1', plistPath], { env });
|
|
61
|
+
}
|
|
62
|
+
for (const key of [
|
|
63
|
+
'AutomationEnabled',
|
|
64
|
+
'IgnoreAXServerEntitlements',
|
|
65
|
+
'AccessibilityEnabled',
|
|
66
|
+
'ApplicationAccessibilityEnabled',
|
|
67
|
+
]) {
|
|
68
|
+
await (0, turtle_spawn_1.default)('plutil', ['-replace', key, '-bool', 'true', plistPath], { env });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (err instanceof eas_build_job_1.ExpoError) {
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
throw new eas_build_job_1.SystemError('Failed to update iOS Simulator accessibility settings.', {
|
|
76
|
+
cause: err,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
IosSimulatorUtils.enableAccessibilitySettingsAsync = enableAccessibilitySettingsAsync;
|
|
40
81
|
async function startAsync({ deviceIdentifier, env, }) {
|
|
41
82
|
const bootstatusResult = await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'bootstatus', deviceIdentifier, '-b'], {
|
|
42
83
|
env,
|
|
@@ -78,8 +119,30 @@ var IosSimulatorUtils;
|
|
|
78
119
|
}
|
|
79
120
|
IosSimulatorUtils.waitForReadyAsync = waitForReadyAsync;
|
|
80
121
|
async function disableApsdAsync({ udid, env, }) {
|
|
81
|
-
|
|
82
|
-
|
|
122
|
+
const launchctlDomains = ['user/foreground', 'system'];
|
|
123
|
+
let lastError;
|
|
124
|
+
for (const domain of launchctlDomains) {
|
|
125
|
+
const service = `${domain}/com.apple.apsd`;
|
|
126
|
+
try {
|
|
127
|
+
await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'spawn', udid, 'launchctl', 'disable', service], { env });
|
|
128
|
+
try {
|
|
129
|
+
await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'spawn', udid, 'launchctl', 'bootout', service], {
|
|
130
|
+
env,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
// bootout can fail when apsd is already gone; verify the service state below.
|
|
135
|
+
lastError = err;
|
|
136
|
+
}
|
|
137
|
+
if (!(await isLaunchctlServiceLoadedAsync({ udid, env, serviceLabel: 'com.apple.apsd' }))) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
lastError = err;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
throw lastError ?? new eas_build_job_1.SystemError('Unable to disable apsd in the Simulator.');
|
|
83
146
|
}
|
|
84
147
|
IosSimulatorUtils.disableApsdAsync = disableApsdAsync;
|
|
85
148
|
async function collectLogsAsync({ deviceIdentifier, env, }) {
|
|
@@ -156,6 +219,22 @@ var IosSimulatorUtils;
|
|
|
156
219
|
}
|
|
157
220
|
IosSimulatorUtils.isDataMigratorProcessRunning = isDataMigratorProcessRunning;
|
|
158
221
|
})(IosSimulatorUtils || (exports.IosSimulatorUtils = IosSimulatorUtils = {}));
|
|
222
|
+
function formatRuntimeDisplayName(runtimeIdentifier) {
|
|
223
|
+
const match = /^com\.apple\.CoreSimulator\.SimRuntime\.([^-]+)-(.+)$/.exec(runtimeIdentifier);
|
|
224
|
+
return match ? `${match[1]} ${match[2].replaceAll('-', '.')}` : runtimeIdentifier;
|
|
225
|
+
}
|
|
226
|
+
async function isLaunchctlServiceLoadedAsync({ udid, env, serviceLabel, }) {
|
|
227
|
+
try {
|
|
228
|
+
await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'spawn', udid, 'launchctl', 'list', serviceLabel], { env });
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
if (err instanceof Error && 'status' in err && err.status === 113) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
throw err;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
159
238
|
function parseUdidFromBootstatusStdout(stdout) {
|
|
160
239
|
const matches = stdout.match(/^Monitoring boot status for .+ \((.+)\)\.$/m);
|
|
161
240
|
if (!matches) {
|
|
@@ -173,6 +173,11 @@ async function isEASUpdateConfigured(ctx) {
|
|
|
173
173
|
}
|
|
174
174
|
async function logDiffFingerprints({ resolvedRuntime, ctx, }) {
|
|
175
175
|
const { resolvedRuntimeVersion, resolvedFingerprintSources } = resolvedRuntime;
|
|
176
|
+
const buildId = ctx.env.EAS_BUILD_ID;
|
|
177
|
+
if (!buildId) {
|
|
178
|
+
ctx.logger.warn('Skipping fingerprint diff because EAS_BUILD_ID is not set');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
176
181
|
const fingerprintInfo = await ctx.graphqlClient
|
|
177
182
|
.query((0, gql_tada_1.graphql)(`
|
|
178
183
|
query GetFingerprintUrl($id: ID!) {
|
|
@@ -184,7 +189,7 @@ async function logDiffFingerprints({ resolvedRuntime, ctx, }) {
|
|
|
184
189
|
}
|
|
185
190
|
}
|
|
186
191
|
}
|
|
187
|
-
`), { id:
|
|
192
|
+
`), { id: buildId })
|
|
188
193
|
.toPromise();
|
|
189
194
|
if (fingerprintInfo.error) {
|
|
190
195
|
ctx.logger.warn('Failed to fetch current fingerprint info', fingerprintInfo.error);
|
|
@@ -20,6 +20,10 @@ async function uploadEmbeddedBundleAsync(ctx) {
|
|
|
20
20
|
ctx.markBuildPhaseSkipped();
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
|
+
if (ctx.job.developmentClient) {
|
|
24
|
+
ctx.markBuildPhaseSkipped();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
23
27
|
const { platform } = ctx.job;
|
|
24
28
|
if (platform === eas_build_job_1.Platform.IOS && ctx.job.simulator) {
|
|
25
29
|
ctx.markBuildPhaseSkipped();
|
package/dist/utils/processes.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getParentAndDescendantProcessPidsAsync = getParentAndDescendantProcessPidsAsync;
|
|
7
|
+
exports.isProcessDescendantOfAsync = isProcessDescendantOfAsync;
|
|
7
8
|
const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
|
|
8
9
|
async function getChildrenPidsAsync(parentPids) {
|
|
9
10
|
try {
|
|
@@ -35,3 +36,25 @@ async function getParentAndDescendantProcessPidsAsync(ppid) {
|
|
|
35
36
|
}
|
|
36
37
|
return [...children];
|
|
37
38
|
}
|
|
39
|
+
async function isProcessDescendantOfAsync(pid, ancestorPid) {
|
|
40
|
+
let currentPid = pid;
|
|
41
|
+
while (currentPid > 0) {
|
|
42
|
+
if (currentPid === ancestorPid) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const result = await (0, turtle_spawn_1.default)('ps', ['-p', String(currentPid), '-o', 'ppid='], {
|
|
47
|
+
stdio: 'pipe',
|
|
48
|
+
});
|
|
49
|
+
const parentPid = Number(result.stdout.toString().trim());
|
|
50
|
+
if (!Number.isInteger(parentPid) || parentPid <= 0 || parentPid === currentPid) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
currentPid = parentPid;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/build-tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "21.0.0",
|
|
4
4
|
"bugs": "https://github.com/expo/eas-cli/issues",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Expo <support@expo.io>",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"watch": "tsc --watch --preserveWatchOutput",
|
|
24
24
|
"prebuild": "yarn gql",
|
|
25
25
|
"build": "tsc",
|
|
26
|
+
"build:record-sim": "mkdir -p bin && record_sim_bin_path=$(swift build -c release --package-path resources/record-sim --build-path resources/record-sim/.build --show-bin-path) && swift build -c release --package-path resources/record-sim --build-path resources/record-sim/.build && cp \"$record_sim_bin_path/record-sim\" bin/record-sim && chmod +x bin/record-sim",
|
|
26
27
|
"typecheck": "tsc",
|
|
27
28
|
"prepack": "rimraf dist \"*.tsbuildinfo\" && yarn gql && tsc -p tsconfig.build.json",
|
|
28
29
|
"jest-unit": "jest --config jest/unit-config.ts",
|
|
@@ -37,17 +38,17 @@
|
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@expo/config": "55.0.10",
|
|
39
40
|
"@expo/config-plugins": "55.0.7",
|
|
40
|
-
"@expo/downloader": "
|
|
41
|
-
"@expo/eas-build-job": "
|
|
41
|
+
"@expo/downloader": "21.0.0",
|
|
42
|
+
"@expo/eas-build-job": "21.0.0",
|
|
42
43
|
"@expo/env": "^0.4.0",
|
|
43
|
-
"@expo/logger": "
|
|
44
|
+
"@expo/logger": "21.0.0",
|
|
44
45
|
"@expo/package-manager": "1.9.10",
|
|
45
46
|
"@expo/plist": "^0.2.0",
|
|
46
47
|
"@expo/results": "^1.0.0",
|
|
47
48
|
"@expo/spawn-async": "1.7.2",
|
|
48
|
-
"@expo/steps": "
|
|
49
|
-
"@expo/template-file": "
|
|
50
|
-
"@expo/turtle-spawn": "
|
|
49
|
+
"@expo/steps": "21.0.0",
|
|
50
|
+
"@expo/template-file": "21.0.0",
|
|
51
|
+
"@expo/turtle-spawn": "21.0.0",
|
|
51
52
|
"@expo/xcpretty": "^4.3.1",
|
|
52
53
|
"@google-cloud/storage": "^7.11.2",
|
|
53
54
|
"@ngrok/ngrok": "1.7.0",
|
|
@@ -100,5 +101,5 @@
|
|
|
100
101
|
"typescript": "^5.5.4",
|
|
101
102
|
"uuid": "^9.0.1"
|
|
102
103
|
},
|
|
103
|
-
"gitHead": "
|
|
104
|
+
"gitHead": "c72cee372f2d45a252acb014493311de85776497"
|
|
104
105
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
|
|
3
|
+
import PackageDescription
|
|
4
|
+
|
|
5
|
+
let package = Package(
|
|
6
|
+
name: "record-sim",
|
|
7
|
+
platforms: [
|
|
8
|
+
.macOS(.v14),
|
|
9
|
+
],
|
|
10
|
+
products: [
|
|
11
|
+
.library(name: "RecordSim", targets: ["RecordSim"]),
|
|
12
|
+
.executable(name: "record-sim", targets: ["record-sim"]),
|
|
13
|
+
],
|
|
14
|
+
targets: [
|
|
15
|
+
.target(
|
|
16
|
+
name: "RecordSim",
|
|
17
|
+
linkerSettings: [
|
|
18
|
+
.linkedFramework("AVFoundation"),
|
|
19
|
+
.linkedFramework("CoreGraphics"),
|
|
20
|
+
.linkedFramework("CoreMedia"),
|
|
21
|
+
.linkedFramework("CoreVideo"),
|
|
22
|
+
.linkedFramework("IOSurface"),
|
|
23
|
+
.linkedFramework("UniformTypeIdentifiers"),
|
|
24
|
+
]
|
|
25
|
+
),
|
|
26
|
+
.executableTarget(name: "record-sim", dependencies: ["RecordSim"]),
|
|
27
|
+
]
|
|
28
|
+
)
|