@expo/build-tools 20.5.1 → 21.0.1
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/builders/custom.js +1 -0
- package/dist/common/installDependencies.js +21 -18
- package/dist/context.js +31 -1
- package/dist/customBuildContext.d.ts +2 -1
- package/dist/customBuildContext.js +4 -0
- package/dist/generic.js +1 -0
- 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/checkout.js +1 -0
- package/dist/steps/functions/createPosthogAnnotation.d.ts +2 -0
- package/dist/steps/functions/createPosthogAnnotation.js +75 -0
- package/dist/steps/functions/finishIosSimulatorRecordings.d.ts +2 -0
- package/dist/steps/functions/finishIosSimulatorRecordings.js +24 -0
- package/dist/steps/functions/installNodeModules.js +1 -0
- package/dist/steps/functions/maestroTests.js +1 -0
- package/dist/steps/functions/rolloutPosthogFlag.d.ts +2 -0
- package/dist/steps/functions/rolloutPosthogFlag.js +208 -0
- 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/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/uploadToAsc.js +1 -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 +1 -0
- package/dist/utils/IosSimulatorUtils.js +41 -2
- package/dist/utils/expoUpdatesEmbedded.js +4 -0
- package/dist/utils/hookMetrics.d.ts +8 -0
- package/dist/utils/hookMetrics.js +18 -0
- package/dist/utils/processes.d.ts +1 -0
- package/dist/utils/processes.js +23 -0
- package/package.json +10 -9
- 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
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRolloutPosthogFlagFunction = createRolloutPosthogFlagFunction;
|
|
4
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
5
|
+
const steps_1 = require("@expo/steps");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const PosthogClient_1 = require("../utils/PosthogClient");
|
|
8
|
+
const PosthogUtils_1 = require("../utils/PosthogUtils");
|
|
9
|
+
const SEARCH_LIMIT = 200;
|
|
10
|
+
const FeatureFlagGroupSchema = zod_1.z.object({ properties: zod_1.z.unknown().optional() }).passthrough();
|
|
11
|
+
const FeatureFlagSchema = zod_1.z
|
|
12
|
+
.object({
|
|
13
|
+
id: zod_1.z.number(),
|
|
14
|
+
key: zod_1.z.string(),
|
|
15
|
+
filters: zod_1.z
|
|
16
|
+
.object({
|
|
17
|
+
groups: zod_1.z.array(FeatureFlagGroupSchema).optional(),
|
|
18
|
+
payloads: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
19
|
+
})
|
|
20
|
+
.passthrough()
|
|
21
|
+
.optional(),
|
|
22
|
+
})
|
|
23
|
+
.passthrough();
|
|
24
|
+
const FlagSearchResponseSchema = zod_1.z.object({ results: zod_1.z.array(zod_1.z.unknown()) });
|
|
25
|
+
function isCatchAllGroup(group) {
|
|
26
|
+
const { properties } = group;
|
|
27
|
+
return (properties === undefined ||
|
|
28
|
+
properties === null ||
|
|
29
|
+
(Array.isArray(properties) && properties.length === 0));
|
|
30
|
+
}
|
|
31
|
+
function createRolloutPosthogFlagFunction() {
|
|
32
|
+
return new steps_1.BuildFunction({
|
|
33
|
+
namespace: 'eas',
|
|
34
|
+
id: 'posthog_flag_rollout',
|
|
35
|
+
name: 'Roll out a PostHog feature flag',
|
|
36
|
+
__metricsId: 'eas/posthog_flag_rollout',
|
|
37
|
+
inputProviders: [
|
|
38
|
+
steps_1.BuildStepInput.createProvider({
|
|
39
|
+
id: 'flag',
|
|
40
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
|
|
41
|
+
required: true,
|
|
42
|
+
}),
|
|
43
|
+
steps_1.BuildStepInput.createProvider({
|
|
44
|
+
id: 'active',
|
|
45
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.BOOLEAN,
|
|
46
|
+
required: false,
|
|
47
|
+
}),
|
|
48
|
+
steps_1.BuildStepInput.createProvider({
|
|
49
|
+
id: 'rollout_percentage',
|
|
50
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.NUMBER,
|
|
51
|
+
required: false,
|
|
52
|
+
}),
|
|
53
|
+
steps_1.BuildStepInput.createProvider({
|
|
54
|
+
id: 'payload',
|
|
55
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.JSON,
|
|
56
|
+
required: false,
|
|
57
|
+
}),
|
|
58
|
+
steps_1.BuildStepInput.createProvider({
|
|
59
|
+
id: 'variant',
|
|
60
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
|
|
61
|
+
required: false,
|
|
62
|
+
}),
|
|
63
|
+
steps_1.BuildStepInput.createProvider({
|
|
64
|
+
id: 'api_key',
|
|
65
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
|
|
66
|
+
required: false,
|
|
67
|
+
}),
|
|
68
|
+
steps_1.BuildStepInput.createProvider({
|
|
69
|
+
id: 'project_id',
|
|
70
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
|
|
71
|
+
required: false,
|
|
72
|
+
}),
|
|
73
|
+
steps_1.BuildStepInput.createProvider({
|
|
74
|
+
id: 'ignore_error',
|
|
75
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.BOOLEAN,
|
|
76
|
+
required: false,
|
|
77
|
+
}),
|
|
78
|
+
],
|
|
79
|
+
fn: async (stepCtx, { inputs, env, signal }) => {
|
|
80
|
+
const { logger } = stepCtx;
|
|
81
|
+
const ignoreError = Boolean(inputs.ignore_error.value);
|
|
82
|
+
const flagKey = inputs.flag.value;
|
|
83
|
+
const active = inputs.active.value;
|
|
84
|
+
const rolloutPercentage = inputs.rollout_percentage.value;
|
|
85
|
+
const payload = inputs.payload.value;
|
|
86
|
+
const variant = inputs.variant.value;
|
|
87
|
+
if (active === undefined && rolloutPercentage === undefined && payload === undefined) {
|
|
88
|
+
throw new eas_build_job_1.UserError('EAS_POSTHOG_FLAG_NO_CHANGE', 'You need to provide "active", "rollout_percentage", or "payload" to change the feature flag.');
|
|
89
|
+
}
|
|
90
|
+
if (rolloutPercentage !== undefined &&
|
|
91
|
+
(!Number.isInteger(rolloutPercentage) || rolloutPercentage < 0 || rolloutPercentage > 100)) {
|
|
92
|
+
throw new eas_build_job_1.UserError('EAS_POSTHOG_FLAG_INVALID_ROLLOUT', `"rollout_percentage" must be an integer between 0 and 100, got ${rolloutPercentage}.`);
|
|
93
|
+
}
|
|
94
|
+
const result = PosthogClient_1.PosthogClient.fromEnv({
|
|
95
|
+
apiKeyOverride: inputs.api_key.value,
|
|
96
|
+
projectIdOverride: inputs.project_id.value,
|
|
97
|
+
env,
|
|
98
|
+
});
|
|
99
|
+
if (!result.client) {
|
|
100
|
+
PosthogUtils_1.PosthogUtils.failOrLogError({
|
|
101
|
+
logger,
|
|
102
|
+
ignoreError,
|
|
103
|
+
error: PosthogUtils_1.PosthogUtils.missingCredentialsError(result.missing),
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const client = result.client;
|
|
108
|
+
await rolloutPosthogFlagAsync({
|
|
109
|
+
logger,
|
|
110
|
+
client,
|
|
111
|
+
flagKey,
|
|
112
|
+
active,
|
|
113
|
+
rolloutPercentage,
|
|
114
|
+
payload,
|
|
115
|
+
variant,
|
|
116
|
+
ignoreError,
|
|
117
|
+
signal,
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async function rolloutPosthogFlagAsync({ logger, client, flagKey, active, rolloutPercentage, payload, variant, ignoreError, signal, }) {
|
|
123
|
+
let searchResponse;
|
|
124
|
+
try {
|
|
125
|
+
searchResponse = await client.requestAsync('GET', `/feature_flags/?limit=${SEARCH_LIMIT}&search=${encodeURIComponent(flagKey)}`, {
|
|
126
|
+
action: `Looking up PostHog flag "${flagKey}"`,
|
|
127
|
+
forbiddenScope: 'feature_flag:read',
|
|
128
|
+
signal,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
PosthogUtils_1.PosthogUtils.failOrLogError({ logger, ignoreError, error });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let results;
|
|
136
|
+
try {
|
|
137
|
+
results = FlagSearchResponseSchema.parse(await searchResponse.json()).results;
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
PosthogUtils_1.PosthogUtils.failOrLogError({
|
|
141
|
+
logger,
|
|
142
|
+
ignoreError,
|
|
143
|
+
error: new eas_build_job_1.UserError('EAS_POSTHOG_FLAG_UNEXPECTED_RESPONSE', `PostHog flag lookup for "${flagKey}" returned an unexpected response.`, { cause: error }),
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const flag = results
|
|
148
|
+
.flatMap(result => {
|
|
149
|
+
const parsed = FeatureFlagSchema.safeParse(result);
|
|
150
|
+
return parsed.success ? [parsed.data] : [];
|
|
151
|
+
})
|
|
152
|
+
.find(candidate => candidate.key === flagKey);
|
|
153
|
+
if (!flag) {
|
|
154
|
+
PosthogUtils_1.PosthogUtils.failOrLogError({
|
|
155
|
+
logger,
|
|
156
|
+
ignoreError,
|
|
157
|
+
error: new eas_build_job_1.UserError('EAS_POSTHOG_FLAG_NOT_FOUND', `No PostHog feature flag with key "${flagKey}" was found. Check the "flag" input.`),
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const patch = {};
|
|
162
|
+
if (active !== undefined) {
|
|
163
|
+
patch.active = active;
|
|
164
|
+
}
|
|
165
|
+
let filters = flag.filters;
|
|
166
|
+
if (rolloutPercentage !== undefined) {
|
|
167
|
+
filters = buildRolloutFilters({ logger, flagKey, filters, rolloutPercentage });
|
|
168
|
+
}
|
|
169
|
+
if (payload !== undefined) {
|
|
170
|
+
filters = {
|
|
171
|
+
...filters,
|
|
172
|
+
payloads: { ...filters?.payloads, [variant ?? 'true']: JSON.stringify(payload) },
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (rolloutPercentage !== undefined || payload !== undefined) {
|
|
176
|
+
patch.filters = filters;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
await client.requestAsync('PATCH', `/feature_flags/${flag.id}/`, {
|
|
180
|
+
action: `Updating PostHog flag "${flagKey}"`,
|
|
181
|
+
forbiddenScope: 'feature_flag:write',
|
|
182
|
+
body: patch,
|
|
183
|
+
signal,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
PosthogUtils_1.PosthogUtils.failOrLogError({ logger, ignoreError, error });
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
logger.info(`Updated PostHog feature flag "${flagKey}".`);
|
|
191
|
+
}
|
|
192
|
+
function buildRolloutFilters({ logger, flagKey, filters, rolloutPercentage, }) {
|
|
193
|
+
const groups = filters?.groups ?? [];
|
|
194
|
+
if (groups.length === 0) {
|
|
195
|
+
return { ...filters, groups: [{ properties: [], rollout_percentage: rolloutPercentage }] };
|
|
196
|
+
}
|
|
197
|
+
const catchAllIndex = groups.findIndex(isCatchAllGroup);
|
|
198
|
+
const hasCatchAll = catchAllIndex >= 0;
|
|
199
|
+
const targetIndex = hasCatchAll ? catchAllIndex : 0;
|
|
200
|
+
if (!hasCatchAll) {
|
|
201
|
+
logger.warn(`Flag "${flagKey}" has no catch-all release-condition group; setting rollout_percentage on the first group, which is condition-scoped.`);
|
|
202
|
+
}
|
|
203
|
+
else if (groups.length > 1) {
|
|
204
|
+
logger.warn(`Flag "${flagKey}" has ${groups.length} release-condition groups; setting rollout_percentage only on the catch-all group to preserve the others.`);
|
|
205
|
+
}
|
|
206
|
+
const groupsWithRollout = groups.map((group, index) => index === targetIndex ? { ...group, rollout_percentage: rolloutPercentage } : group);
|
|
207
|
+
return { ...filters, groups: groupsWithRollout };
|
|
208
|
+
}
|
|
@@ -11,12 +11,17 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
11
11
|
const node_os_1 = __importDefault(require("node:os"));
|
|
12
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const sentry_1 = require("../../sentry");
|
|
14
|
+
const agentDeviceArtifacts_1 = require("../utils/agentDeviceArtifacts");
|
|
14
15
|
const remoteDeviceRunSession_1 = require("../utils/remoteDeviceRunSession");
|
|
15
16
|
const AGENT_DEVICE_PACKAGE_NAME = 'agent-device';
|
|
16
|
-
const AGENT_DEVICE_REPO_URL = 'https://github.com/
|
|
17
|
+
const AGENT_DEVICE_REPO_URL = 'https://github.com/callstack/agent-device.git';
|
|
17
18
|
const SRC_DIR = '/tmp/agent-device-src';
|
|
18
19
|
const DAEMON_JSON_PATH = node_path_1.default.join(node_os_1.default.homedir(), '.agent-device', 'daemon.json');
|
|
19
20
|
const STARTUP_TIMEOUT_MS = 60_000;
|
|
21
|
+
const AGENT_DEVICE_DAEMON_ENV = {
|
|
22
|
+
AGENT_DEVICE_DAEMON_SERVER_MODE: 'http',
|
|
23
|
+
AGENT_DEVICE_RETAIN_ARTIFACTS: '1',
|
|
24
|
+
};
|
|
20
25
|
function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
21
26
|
return new steps_1.BuildFunction({
|
|
22
27
|
namespace: 'eas',
|
|
@@ -30,7 +35,7 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
|
30
35
|
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
|
|
31
36
|
}),
|
|
32
37
|
],
|
|
33
|
-
fn: async ({ logger, global }, { inputs, env }) => {
|
|
38
|
+
fn: async ({ logger, global }, { inputs, env, signal }) => {
|
|
34
39
|
// Fail fast before any expensive setup if the injected env
|
|
35
40
|
// vars are missing: DEVICE_RUN_SESSION_ID (to report the remote config
|
|
36
41
|
// back to the API server), EAS_SIMULATOR_NGROK_TUNNEL_DOMAIN (base domain
|
|
@@ -82,10 +87,18 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
|
82
87
|
},
|
|
83
88
|
logger,
|
|
84
89
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
void (0, agentDeviceArtifacts_1.pollAgentDeviceArtifactsForUploadAsync)(ctx, {
|
|
91
|
+
deviceRunSessionId,
|
|
92
|
+
daemonUrl: `http://127.0.0.1:${daemonPort}`,
|
|
93
|
+
daemonToken,
|
|
94
|
+
logger,
|
|
95
|
+
});
|
|
96
|
+
await (0, remoteDeviceRunSession_1.waitForDeviceRunSessionStoppedAsync)({
|
|
97
|
+
ctx,
|
|
98
|
+
deviceRunSessionId,
|
|
99
|
+
logger,
|
|
100
|
+
signal,
|
|
101
|
+
});
|
|
89
102
|
},
|
|
90
103
|
});
|
|
91
104
|
}
|
|
@@ -105,7 +118,7 @@ async function startAgentDeviceDaemonAsync({ packageVersion, env, logger, }) {
|
|
|
105
118
|
return (0, remoteDeviceRunSession_1.spawnDetached)({
|
|
106
119
|
command: 'node',
|
|
107
120
|
args: [daemonPath],
|
|
108
|
-
env: { ...env,
|
|
121
|
+
env: { ...env, ...AGENT_DEVICE_DAEMON_ENV },
|
|
109
122
|
});
|
|
110
123
|
}
|
|
111
124
|
catch (err) {
|
|
@@ -144,7 +157,7 @@ async function startAgentDeviceDaemonFromGitAsync({ packageVersion, env, logger,
|
|
|
144
157
|
command: 'bun',
|
|
145
158
|
args: ['run', 'src/daemon.ts'],
|
|
146
159
|
cwd: SRC_DIR,
|
|
147
|
-
env: { ...env,
|
|
160
|
+
env: { ...env, ...AGENT_DEVICE_DAEMON_ENV },
|
|
148
161
|
});
|
|
149
162
|
}
|
|
150
163
|
async function cloneAgentDeviceAsync({ packageVersion, env, logger, }) {
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { type bunyan } from '@expo/logger';
|
|
2
2
|
import { BuildFunction } from '@expo/steps';
|
|
3
|
+
import { z } from 'zod';
|
|
3
4
|
import { CustomBuildContext } from '../../customBuildContext';
|
|
4
5
|
export declare const MIN_ARGENT_REMOTE_SESSION_VERSION = "0.12.0";
|
|
6
|
+
declare const ArgentToolServerStateSchema: z.ZodObject<{
|
|
7
|
+
port: z.ZodNumber;
|
|
8
|
+
pid: z.ZodNumber;
|
|
9
|
+
token: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
type ArgentToolServerState = z.infer<typeof ArgentToolServerStateSchema>;
|
|
5
12
|
export declare function createStartArgentRemoteSessionBuildFunction(ctx: CustomBuildContext): BuildFunction;
|
|
6
13
|
export declare function warnIfArgentPackageVersionCannotBeVerified({ packageVersion, logger, }: {
|
|
7
14
|
packageVersion: string | undefined;
|
|
8
15
|
logger: bunyan;
|
|
9
16
|
}): void;
|
|
17
|
+
export declare function waitForArgentToolServerStateAsync({ stateDir, ancestorPid, timeoutMs, pollIntervalMs, }: {
|
|
18
|
+
stateDir: string;
|
|
19
|
+
ancestorPid: number;
|
|
20
|
+
timeoutMs: number;
|
|
21
|
+
pollIntervalMs?: number;
|
|
22
|
+
}): Promise<ArgentToolServerState>;
|
|
23
|
+
export {};
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.MIN_ARGENT_REMOTE_SESSION_VERSION = void 0;
|
|
7
7
|
exports.createStartArgentRemoteSessionBuildFunction = createStartArgentRemoteSessionBuildFunction;
|
|
8
8
|
exports.warnIfArgentPackageVersionCannotBeVerified = warnIfArgentPackageVersionCannotBeVerified;
|
|
9
|
+
exports.waitForArgentToolServerStateAsync = waitForArgentToolServerStateAsync;
|
|
9
10
|
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
10
11
|
const steps_1 = require("@expo/steps");
|
|
11
12
|
const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
|
|
@@ -14,15 +15,19 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
14
15
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
16
|
const semver_1 = __importDefault(require("semver"));
|
|
16
17
|
const zod_1 = require("zod");
|
|
18
|
+
const sentry_1 = require("../../sentry");
|
|
19
|
+
const processes_1 = require("../../utils/processes");
|
|
20
|
+
const retry_1 = require("../../utils/retry");
|
|
17
21
|
const argentArtifacts_1 = require("../utils/argentArtifacts");
|
|
18
22
|
const remoteDeviceRunSession_1 = require("../utils/remoteDeviceRunSession");
|
|
19
23
|
const ARGENT_PACKAGE_NAME = '@swmansion/argent';
|
|
20
24
|
exports.MIN_ARGENT_REMOTE_SESSION_VERSION = '0.12.0';
|
|
21
25
|
const ARGENT_ARTIFACTS_LIST_ENDPOINT_FLAG = 'artifacts-list-endpoint';
|
|
22
|
-
const
|
|
26
|
+
const ARGENT_STATE_DIR = node_path_1.default.join(node_os_1.default.homedir(), '.argent');
|
|
23
27
|
const STARTUP_TIMEOUT_MS = 60_000;
|
|
24
28
|
const ArgentToolServerStateSchema = zod_1.z.object({
|
|
25
29
|
port: zod_1.z.number(),
|
|
30
|
+
pid: zod_1.z.number(),
|
|
26
31
|
token: zod_1.z.string().optional(),
|
|
27
32
|
});
|
|
28
33
|
function createStartArgentRemoteSessionBuildFunction(ctx) {
|
|
@@ -38,7 +43,7 @@ function createStartArgentRemoteSessionBuildFunction(ctx) {
|
|
|
38
43
|
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
|
|
39
44
|
}),
|
|
40
45
|
],
|
|
41
|
-
fn: async ({ logger, global }, { inputs, env }) => {
|
|
46
|
+
fn: async ({ logger, global }, { inputs, env, signal }) => {
|
|
42
47
|
// Fail fast before any expensive setup if the injected env
|
|
43
48
|
// vars are missing: DEVICE_RUN_SESSION_ID (to report the remote config
|
|
44
49
|
// back to the API server), EAS_SIMULATOR_NGROK_TUNNEL_DOMAIN (base domain
|
|
@@ -54,11 +59,11 @@ function createStartArgentRemoteSessionBuildFunction(ctx) {
|
|
|
54
59
|
if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
|
|
55
60
|
await (0, remoteDeviceRunSession_1.selectXcodeDeveloperDirectoryAsync)({ env, logger });
|
|
56
61
|
}
|
|
57
|
-
// Stale state from a previous run would mask the new server's port.
|
|
58
|
-
await node_fs_1.default.promises.rm(ARGENT_STATE_FILE, { force: true });
|
|
59
62
|
logger.info('Enabling the Argent artifacts list endpoint flag.');
|
|
60
63
|
await (0, turtle_spawn_1.default)('bunx', [`${ARGENT_PACKAGE_NAME}@${versionSpec}`, 'enable', ARGENT_ARTIFACTS_LIST_ENDPOINT_FLAG], { env, logger });
|
|
61
64
|
logger.info(`Launching ${ARGENT_PACKAGE_NAME}@${versionSpec} tool-server via bunx.`);
|
|
65
|
+
// Keep Argent itself in foreground mode under the detached bunx process. This preserves
|
|
66
|
+
// the bunx -> Argent CLI -> tool-server ancestry used to identify the matching state file.
|
|
62
67
|
const argentServer = (0, remoteDeviceRunSession_1.spawnDetached)({
|
|
63
68
|
command: 'bunx',
|
|
64
69
|
args: [
|
|
@@ -69,19 +74,21 @@ function createStartArgentRemoteSessionBuildFunction(ctx) {
|
|
|
69
74
|
'0',
|
|
70
75
|
'--idle-timeout',
|
|
71
76
|
'0',
|
|
72
|
-
'--
|
|
77
|
+
'--force',
|
|
73
78
|
],
|
|
74
79
|
env,
|
|
75
80
|
});
|
|
76
|
-
|
|
81
|
+
if (argentServer.pid === undefined) {
|
|
82
|
+
throw new eas_build_job_1.SystemError('Failed to start Argent: could not determine the PID of the launched process.');
|
|
83
|
+
}
|
|
84
|
+
logger.info(`Waiting for argent tool-server state in ${ARGENT_STATE_DIR}.`);
|
|
77
85
|
let toolServerPort;
|
|
78
86
|
let toolServerToken;
|
|
79
87
|
try {
|
|
80
|
-
const toolServerState = await (
|
|
81
|
-
|
|
88
|
+
const toolServerState = await waitForArgentToolServerStateAsync({
|
|
89
|
+
stateDir: ARGENT_STATE_DIR,
|
|
90
|
+
ancestorPid: argentServer.pid,
|
|
82
91
|
timeoutMs: STARTUP_TIMEOUT_MS,
|
|
83
|
-
description: 'argent tool-server state',
|
|
84
|
-
parse: parseArgentToolServerState,
|
|
85
92
|
});
|
|
86
93
|
toolServerPort = toolServerState.port;
|
|
87
94
|
toolServerToken = toolServerState.token;
|
|
@@ -91,47 +98,67 @@ function createStartArgentRemoteSessionBuildFunction(ctx) {
|
|
|
91
98
|
throw new eas_build_job_1.SystemError(`${err instanceof Error ? err.message : `Timed out waiting for argent tool-server state.`}${output ? `\nArgent tool-server output:\n${output}` : ''}`);
|
|
92
99
|
}
|
|
93
100
|
logger.info(`Argent tool-server is listening on port ${toolServerPort}.`);
|
|
94
|
-
|
|
101
|
+
const artifactPollAbortController = new AbortController();
|
|
102
|
+
const artifactPollSignal = signal
|
|
103
|
+
? AbortSignal.any([signal, artifactPollAbortController.signal])
|
|
104
|
+
: artifactPollAbortController.signal;
|
|
105
|
+
const artifactPollingPromise = (0, argentArtifacts_1.pollArgentArtifactsForUploadAsync)(ctx, {
|
|
95
106
|
deviceRunSessionId,
|
|
96
107
|
toolsUrl: `http://127.0.0.1:${toolServerPort}`,
|
|
97
108
|
toolsAuthToken: toolServerToken,
|
|
98
109
|
logger,
|
|
110
|
+
signal: artifactPollSignal,
|
|
99
111
|
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
authtoken: ngrokAuthtoken,
|
|
105
|
-
rewriteHostHeader: true,
|
|
106
|
-
logger,
|
|
107
|
-
});
|
|
108
|
-
logger.info(`Tunnel is ready at ${publicToolsUrl}.`);
|
|
109
|
-
// serve-sim is iOS-only — Android sessions go without a preview URL.
|
|
110
|
-
let webPreviewUrl;
|
|
111
|
-
if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
|
|
112
|
-
const serveSim = await (0, remoteDeviceRunSession_1.startServeSimWithTunnelAsync)(ctx, {
|
|
112
|
+
try {
|
|
113
|
+
const publicToolsUrl = await (0, remoteDeviceRunSession_1.startNgrokTunnelAsync)({
|
|
114
|
+
port: toolServerPort,
|
|
115
|
+
subdomainPrefix: 'argent',
|
|
113
116
|
baseDomain: ngrokTunnelDomain,
|
|
114
|
-
|
|
117
|
+
authtoken: ngrokAuthtoken,
|
|
118
|
+
rewriteHostHeader: true,
|
|
115
119
|
logger,
|
|
116
|
-
timeoutMs: STARTUP_TIMEOUT_MS,
|
|
117
120
|
});
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
logger.info(`Tunnel is ready at ${publicToolsUrl}.`);
|
|
122
|
+
// serve-sim is iOS-only — Android sessions go without a preview URL.
|
|
123
|
+
let webPreviewUrl;
|
|
124
|
+
if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
|
|
125
|
+
const serveSim = await (0, remoteDeviceRunSession_1.startServeSimWithTunnelAsync)(ctx, {
|
|
126
|
+
baseDomain: ngrokTunnelDomain,
|
|
127
|
+
env,
|
|
128
|
+
logger,
|
|
129
|
+
timeoutMs: STARTUP_TIMEOUT_MS,
|
|
130
|
+
});
|
|
131
|
+
webPreviewUrl = serveSim.previewUrl;
|
|
132
|
+
logger.info(`Web preview URL: ${webPreviewUrl}`);
|
|
133
|
+
}
|
|
134
|
+
await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
|
|
135
|
+
ctx,
|
|
136
|
+
deviceRunSessionId,
|
|
137
|
+
remoteConfig: {
|
|
138
|
+
toolsUrl: publicToolsUrl,
|
|
139
|
+
...(toolServerToken ? { toolsAuthToken: toolServerToken } : {}),
|
|
140
|
+
...(webPreviewUrl ? { webPreviewUrl } : {}),
|
|
141
|
+
},
|
|
142
|
+
logger,
|
|
143
|
+
});
|
|
144
|
+
await (0, remoteDeviceRunSession_1.waitForDeviceRunSessionStoppedAsync)({
|
|
145
|
+
ctx,
|
|
146
|
+
deviceRunSessionId,
|
|
147
|
+
logger,
|
|
148
|
+
signal,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
artifactPollAbortController.abort();
|
|
153
|
+
try {
|
|
154
|
+
await artifactPollingPromise;
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
158
|
+
sentry_1.Sentry.capture('Could not finish Argent remote session artifact polling', error);
|
|
159
|
+
logger.warn({ err: error }, 'Could not finish Argent remote session artifact polling.');
|
|
160
|
+
}
|
|
120
161
|
}
|
|
121
|
-
await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
|
|
122
|
-
ctx,
|
|
123
|
-
deviceRunSessionId,
|
|
124
|
-
remoteConfig: {
|
|
125
|
-
toolsUrl: publicToolsUrl,
|
|
126
|
-
...(toolServerToken ? { toolsAuthToken: toolServerToken } : {}),
|
|
127
|
-
...(webPreviewUrl ? { webPreviewUrl } : {}),
|
|
128
|
-
},
|
|
129
|
-
logger,
|
|
130
|
-
});
|
|
131
|
-
logger.info('Remote session is live. Keeping the job alive until the session is stopped.');
|
|
132
|
-
// Keep the turtle job alive so the tool-server and tunnel stay reachable
|
|
133
|
-
// until stopDeviceRunSession cancels the run.
|
|
134
|
-
await new Promise(() => { });
|
|
135
162
|
},
|
|
136
163
|
});
|
|
137
164
|
}
|
|
@@ -153,9 +180,43 @@ function warnIfArgentPackageVersionCannotBeVerified({ packageVersion, logger, })
|
|
|
153
180
|
}
|
|
154
181
|
}
|
|
155
182
|
function parseArgentToolServerState(raw) {
|
|
156
|
-
|
|
183
|
+
let json;
|
|
184
|
+
try {
|
|
185
|
+
json = JSON.parse(raw);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
throw new eas_build_job_1.SystemError('Expected tool-server state to contain valid JSON.', { cause: err });
|
|
189
|
+
}
|
|
190
|
+
const result = ArgentToolServerStateSchema.safeParse(json);
|
|
157
191
|
if (!result.success) {
|
|
158
|
-
throw new eas_build_job_1.SystemError(`Expected tool-server state to contain { "port": <number>, ... }: ${result.error.message}`);
|
|
192
|
+
throw new eas_build_job_1.SystemError(`Expected tool-server state to contain { "port": <number>, "pid": <number>, ... }: ${result.error.message}`);
|
|
159
193
|
}
|
|
160
194
|
return result.data;
|
|
161
195
|
}
|
|
196
|
+
async function waitForArgentToolServerStateAsync({ stateDir, ancestorPid, timeoutMs, pollIntervalMs = 1_000, }) {
|
|
197
|
+
const deadline = Date.now() + timeoutMs;
|
|
198
|
+
let lastError;
|
|
199
|
+
while (Date.now() < deadline) {
|
|
200
|
+
try {
|
|
201
|
+
const stateFileNames = (await node_fs_1.default.promises.readdir(stateDir)).filter(name => name.startsWith('tool-server') && name.endsWith('.json'));
|
|
202
|
+
for (const stateFileName of stateFileNames) {
|
|
203
|
+
try {
|
|
204
|
+
const state = parseArgentToolServerState(await node_fs_1.default.promises.readFile(node_path_1.default.join(stateDir, stateFileName), 'utf8'));
|
|
205
|
+
if (await (0, processes_1.isProcessDescendantOfAsync)(state.pid, ancestorPid)) {
|
|
206
|
+
return state;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
if (err.code !== 'ENOENT' && !(err instanceof eas_build_job_1.SystemError)) {
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
lastError = err;
|
|
218
|
+
}
|
|
219
|
+
await (0, retry_1.sleepAsync)(pollIntervalMs);
|
|
220
|
+
}
|
|
221
|
+
throw new eas_build_job_1.SystemError(`Timed out waiting for an argent tool-server state file belonging to process ${ancestorPid} in ${stateDir}${lastError instanceof Error ? `: ${lastError.message}` : ''}.`);
|
|
222
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createStartIosSimulatorRecordingsBuildFunction = createStartIosSimulatorRecordingsBuildFunction;
|
|
4
|
+
const steps_1 = require("@expo/steps");
|
|
5
|
+
const IosSimulatorRecordingUtils_1 = require("../utils/IosSimulatorRecordingUtils");
|
|
6
|
+
function createStartIosSimulatorRecordingsBuildFunction() {
|
|
7
|
+
return new steps_1.BuildFunction({
|
|
8
|
+
namespace: 'eas',
|
|
9
|
+
id: 'start_ios_simulator_recordings',
|
|
10
|
+
name: 'Start iOS Simulator recordings',
|
|
11
|
+
__metricsId: 'eas/start_ios_simulator_recordings',
|
|
12
|
+
supportedRuntimePlatforms: [steps_1.BuildRuntimePlatform.DARWIN],
|
|
13
|
+
fn: async ({ logger }, { env }) => {
|
|
14
|
+
await IosSimulatorRecordingUtils_1.IosSimulatorRecordingUtils.startAsync({
|
|
15
|
+
env,
|
|
16
|
+
logger,
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -11,7 +11,7 @@ function createStartServeSimRemoteSessionBuildFunction(ctx) {
|
|
|
11
11
|
name: 'Start serve-sim remote session',
|
|
12
12
|
__metricsId: 'eas/start_serve_sim_remote_session',
|
|
13
13
|
supportedRuntimePlatforms: [steps_1.BuildRuntimePlatform.DARWIN],
|
|
14
|
-
fn: async ({ logger }, { env }) => {
|
|
14
|
+
fn: async ({ logger }, { env, signal }) => {
|
|
15
15
|
const deviceRunSessionId = (0, remoteDeviceRunSession_1.getDeviceRunSessionIdOrThrow)(env);
|
|
16
16
|
const ngrokTunnelDomain = (0, remoteDeviceRunSession_1.getNgrokTunnelDomainOrThrow)(env);
|
|
17
17
|
logger.info('Starting serve-sim remote session.');
|
|
@@ -30,10 +30,12 @@ function createStartServeSimRemoteSessionBuildFunction(ctx) {
|
|
|
30
30
|
remoteConfig: { previewUrl, streamUrl },
|
|
31
31
|
logger,
|
|
32
32
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
await (0, remoteDeviceRunSession_1.waitForDeviceRunSessionStoppedAsync)({
|
|
34
|
+
ctx,
|
|
35
|
+
deviceRunSessionId,
|
|
36
|
+
logger,
|
|
37
|
+
signal,
|
|
38
|
+
});
|
|
37
39
|
},
|
|
38
40
|
});
|
|
39
41
|
}
|