@expo/build-tools 19.0.3 → 19.0.6

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/ios/pod.js CHANGED
@@ -8,7 +8,7 @@ const eas_build_job_1 = require("@expo/eas-build-job");
8
8
  const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const semver_1 = __importDefault(require("semver"));
11
- const MIN_PRECOMPILED_MODULES_EXPO_VERSION = '55.0.21';
11
+ const MIN_PRECOMPILED_MODULES_EXPO_VERSION = '55.0.26';
12
12
  const PRECOMPILED_MODULES_BASE_URL = 'https://storage.googleapis.com/eas-build-precompiled-modules/';
13
13
  async function installPods(ctx, { infoCallbackFn }) {
14
14
  const iosDir = path_1.default.join(ctx.getReactNativeProjectDirectory(), 'ios');
@@ -39,6 +39,7 @@ const saveCache_1 = require("./functions/saveCache");
39
39
  const sendSlackMessage_1 = require("./functions/sendSlackMessage");
40
40
  const startAgentDeviceRemoteSession_1 = require("./functions/startAgentDeviceRemoteSession");
41
41
  const startAndroidEmulator_1 = require("./functions/startAndroidEmulator");
42
+ const startArgentRemoteSession_1 = require("./functions/startArgentRemoteSession");
42
43
  const startCuttlefishDevice_1 = require("./functions/startCuttlefishDevice");
43
44
  const startIosSimulator_1 = require("./functions/startIosSimulator");
44
45
  const startServeSimRemoteSession_1 = require("./functions/startServeSimRemoteSession");
@@ -77,6 +78,7 @@ function getEasFunctions(ctx) {
77
78
  (0, runFastlane_1.runFastlaneFunction)(),
78
79
  (0, parseXcactivitylog_1.parseXcactivitylogFunction)(),
79
80
  (0, startAgentDeviceRemoteSession_1.createStartAgentDeviceRemoteSessionBuildFunction)(ctx),
81
+ (0, startArgentRemoteSession_1.createStartArgentRemoteSessionBuildFunction)(ctx),
80
82
  (0, startAndroidEmulator_1.createStartAndroidEmulatorBuildFunction)(),
81
83
  (0, startCuttlefishDevice_1.createStartCuttlefishDeviceBuildFunction)(),
82
84
  (0, startIosSimulator_1.createStartIosSimulatorBuildFunction)(),
@@ -7,16 +7,13 @@ exports.createStartAgentDeviceRemoteSessionBuildFunction = createStartAgentDevic
7
7
  const eas_build_job_1 = require("@expo/eas-build-job");
8
8
  const steps_1 = require("@expo/steps");
9
9
  const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
10
- const node_fs_1 = __importDefault(require("node:fs"));
11
10
  const node_os_1 = __importDefault(require("node:os"));
12
11
  const node_path_1 = __importDefault(require("node:path"));
13
- const retry_1 = require("../../utils/retry");
14
12
  const remoteDeviceRunSession_1 = require("../utils/remoteDeviceRunSession");
15
13
  const AGENT_DEVICE_REPO_URL = 'https://github.com/callstackincubator/agent-device.git';
16
14
  const SRC_DIR = '/tmp/agent-device-src';
17
15
  const DAEMON_JSON_PATH = node_path_1.default.join(node_os_1.default.homedir(), '.agent-device', 'daemon.json');
18
16
  const XCODE_DEVELOPER_DIR = '/Applications/Xcode.app/Contents/Developer';
19
- const CLOUDFLARED_LINUX_INSTALL_PATH = '/usr/local/bin/cloudflared';
20
17
  const STARTUP_TIMEOUT_MS = 60_000;
21
18
  function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
22
19
  return new steps_1.BuildFunction({
@@ -44,7 +41,7 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
44
41
  await (0, turtle_spawn_1.default)('sudo', ['xcode-select', '-s', XCODE_DEVELOPER_DIR], { env, logger });
45
42
  }
46
43
  logger.info('Ensuring cloudflared is installed.');
47
- const cloudflaredCommand = await ensureCloudflaredInstalledAsync({
44
+ const cloudflaredCommand = await (0, remoteDeviceRunSession_1.ensureCloudflaredInstalledAsync)({
48
45
  runtimePlatform,
49
46
  env,
50
47
  logger,
@@ -67,12 +64,12 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
67
64
  env: { ...env, AGENT_DEVICE_DAEMON_SERVER_MODE: 'http' },
68
65
  });
69
66
  logger.info(`Waiting for daemon credentials at ${DAEMON_JSON_PATH}.`);
70
- await waitForFileAsync({
67
+ const { port: daemonPort, token: daemonToken } = await (0, remoteDeviceRunSession_1.waitForFileAsync)({
71
68
  filePath: DAEMON_JSON_PATH,
72
69
  timeoutMs: STARTUP_TIMEOUT_MS,
73
- description: 'agent-device daemon',
70
+ description: 'agent-device daemon credentials',
71
+ parse: parseDaemonInfo,
74
72
  });
75
- const { port: daemonPort, token: daemonToken } = readDaemonInfo(DAEMON_JSON_PATH);
76
73
  logger.info(`Daemon is listening on port ${daemonPort}; loaded auth token.`);
77
74
  logger.info(`Starting cloudflared tunnel to http://localhost:${daemonPort}.`);
78
75
  const cloudflared = (0, remoteDeviceRunSession_1.spawnDetached)({
@@ -81,7 +78,7 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
81
78
  env,
82
79
  });
83
80
  logger.info('Waiting for a public tunnel URL.');
84
- const agentDeviceRemoteSessionUrl = await waitForMatchInOutputAsync({
81
+ const agentDeviceRemoteSessionUrl = await (0, remoteDeviceRunSession_1.waitForMatchInOutputAsync)({
85
82
  process: cloudflared,
86
83
  pattern: /https:\/\/[a-z0-9-]+\.trycloudflare\.com/,
87
84
  timeoutMs: STARTUP_TIMEOUT_MS,
@@ -117,44 +114,6 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
117
114
  },
118
115
  });
119
116
  }
120
- async function ensureCloudflaredInstalledAsync({ runtimePlatform, env, logger, }) {
121
- if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
122
- await (0, remoteDeviceRunSession_1.ensureBrewPackageInstalledAsync)({ name: 'cloudflared', env, logger });
123
- return 'cloudflared';
124
- }
125
- if (await isCommandAvailableAsync({ command: 'cloudflared', env })) {
126
- return 'cloudflared';
127
- }
128
- const cloudflaredArch = cloudflaredLinuxArchForNodeArch(node_os_1.default.arch());
129
- const downloadUrl = `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cloudflaredArch}`;
130
- logger.info(`Downloading cloudflared from ${downloadUrl} to ${CLOUDFLARED_LINUX_INSTALL_PATH}.`);
131
- await (0, turtle_spawn_1.default)('sudo', ['curl', '-fsSL', '-o', CLOUDFLARED_LINUX_INSTALL_PATH, downloadUrl], {
132
- env,
133
- logger,
134
- });
135
- await (0, turtle_spawn_1.default)('sudo', ['chmod', '+x', CLOUDFLARED_LINUX_INSTALL_PATH], { env, logger });
136
- // Return the absolute install path so the tunnel command works even when
137
- // /usr/local/bin is not on the step's PATH.
138
- return CLOUDFLARED_LINUX_INSTALL_PATH;
139
- }
140
- function cloudflaredLinuxArchForNodeArch(arch) {
141
- if (arch === 'x64') {
142
- return 'amd64';
143
- }
144
- if (arch === 'arm64') {
145
- return 'arm64';
146
- }
147
- throw new eas_build_job_1.SystemError(`Unsupported architecture for cloudflared on Linux: "${arch}". Expected "x64" or "arm64".`);
148
- }
149
- async function isCommandAvailableAsync({ command, env, }) {
150
- try {
151
- await (0, turtle_spawn_1.default)('bash', ['-c', `command -v ${command}`], { env, ignoreStdio: true });
152
- return true;
153
- }
154
- catch {
155
- return false;
156
- }
157
- }
158
117
  async function cloneAgentDeviceAsync({ packageVersion, env, logger, }) {
159
118
  const branchArgs = packageVersion ? ['--branch', `v${packageVersion}`] : [];
160
119
  await (0, turtle_spawn_1.default)('git', ['clone', '--depth', '1', ...branchArgs, AGENT_DEVICE_REPO_URL, SRC_DIR], {
@@ -162,39 +121,13 @@ async function cloneAgentDeviceAsync({ packageVersion, env, logger, }) {
162
121
  logger,
163
122
  });
164
123
  }
165
- async function waitForMatchInOutputAsync({ process, pattern, timeoutMs, description, }) {
166
- const deadline = Date.now() + timeoutMs;
167
- while (Date.now() < deadline) {
168
- const match = pattern.exec(process.getOutput());
169
- if (match) {
170
- return match[1] ?? match[0];
171
- }
172
- await (0, retry_1.sleepAsync)(1_000);
173
- }
174
- throw new eas_build_job_1.SystemError(`Timed out waiting for ${description} to start. Last output:\n${process.getOutput() || '<empty>'}`);
175
- }
176
- async function waitForFileAsync({ filePath, timeoutMs, description, }) {
177
- const deadline = Date.now() + timeoutMs;
178
- while (Date.now() < deadline) {
179
- try {
180
- await node_fs_1.default.promises.access(filePath);
181
- return;
182
- }
183
- catch {
184
- // not yet; keep polling
185
- }
186
- await (0, retry_1.sleepAsync)(1_000);
187
- }
188
- throw new eas_build_job_1.SystemError(`Timed out waiting for ${description} to write ${filePath}.`);
189
- }
190
- function readDaemonInfo(filePath) {
191
- const raw = node_fs_1.default.readFileSync(filePath, 'utf8');
124
+ function parseDaemonInfo(raw) {
192
125
  const parsed = JSON.parse(raw);
193
126
  if (!parsed ||
194
127
  typeof parsed !== 'object' ||
195
128
  typeof parsed.httpPort !== 'number' ||
196
129
  typeof parsed.token !== 'string') {
197
- throw new eas_build_job_1.SystemError(`Expected ${filePath} to contain { "httpPort": <number>, "token": "..." }.`);
130
+ throw new eas_build_job_1.SystemError('Expected daemon credentials to contain { "httpPort": <number>, "token": "..." }.');
198
131
  }
199
132
  const { httpPort, token } = parsed;
200
133
  return { port: httpPort, token };
@@ -0,0 +1,3 @@
1
+ import { BuildFunction } from '@expo/steps';
2
+ import { CustomBuildContext } from '../../customBuildContext';
3
+ export declare function createStartArgentRemoteSessionBuildFunction(ctx: CustomBuildContext): BuildFunction;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createStartArgentRemoteSessionBuildFunction = createStartArgentRemoteSessionBuildFunction;
7
+ const eas_build_job_1 = require("@expo/eas-build-job");
8
+ const steps_1 = require("@expo/steps");
9
+ const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
10
+ const node_fs_1 = __importDefault(require("node:fs"));
11
+ const node_os_1 = __importDefault(require("node:os"));
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const zod_1 = require("zod");
14
+ const remoteDeviceRunSession_1 = require("../utils/remoteDeviceRunSession");
15
+ const ARGENT_PACKAGE_NAME = '@swmansion/argent';
16
+ const ARGENT_STATE_FILE = node_path_1.default.join(node_os_1.default.homedir(), '.argent', 'tool-server.json');
17
+ const XCODE_DEVELOPER_DIR = '/Applications/Xcode.app/Contents/Developer';
18
+ const STARTUP_TIMEOUT_MS = 60_000;
19
+ const ArgentToolServerStateSchema = zod_1.z.object({ port: zod_1.z.number() });
20
+ function createStartArgentRemoteSessionBuildFunction(ctx) {
21
+ return new steps_1.BuildFunction({
22
+ namespace: 'eas',
23
+ id: 'start_argent_remote_session',
24
+ name: 'Start argent remote session',
25
+ __metricsId: 'eas/start_argent_remote_session',
26
+ inputProviders: [
27
+ steps_1.BuildStepInput.createProvider({
28
+ id: 'package_version',
29
+ required: false,
30
+ allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
31
+ }),
32
+ ],
33
+ fn: async ({ logger, global }, { inputs, env }) => {
34
+ // Fail fast before any expensive setup if the orchestrator-injected
35
+ // DEVICE_RUN_SESSION_ID env var is missing — without it we cannot
36
+ // report the remote config back to the API server.
37
+ const deviceRunSessionId = (0, remoteDeviceRunSession_1.getDeviceRunSessionIdOrThrow)(env);
38
+ const packageVersion = inputs.package_version.value;
39
+ const versionSpec = packageVersion ?? 'latest';
40
+ const { runtimePlatform } = global;
41
+ logger.info(`Starting argent remote session (version: ${versionSpec}, runtime: ${runtimePlatform}).`);
42
+ if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
43
+ logger.info(`Selecting Xcode developer directory: ${XCODE_DEVELOPER_DIR}.`);
44
+ await (0, turtle_spawn_1.default)('sudo', ['xcode-select', '-s', XCODE_DEVELOPER_DIR], { env, logger });
45
+ }
46
+ logger.info('Ensuring cloudflared is installed.');
47
+ const cloudflaredCommand = await (0, remoteDeviceRunSession_1.ensureCloudflaredInstalledAsync)({
48
+ runtimePlatform,
49
+ env,
50
+ logger,
51
+ });
52
+ // Stale state from a previous run would mask the new server's port.
53
+ await node_fs_1.default.promises.rm(ARGENT_STATE_FILE, { force: true });
54
+ logger.info(`Launching ${ARGENT_PACKAGE_NAME}@${versionSpec} via bunx.`);
55
+ // `argent mcp` is the public entry that triggers @argent/tools-client
56
+ // to spawn the tool-server detached + unref'd, so the tool-server
57
+ // outlives this MCP process. ARGENT_IDLE_TIMEOUT_MINUTES=0 disables the
58
+ // 30-min idle shutdown that would otherwise tear the tunnel down.
59
+ (0, remoteDeviceRunSession_1.spawnDetached)({
60
+ command: 'bunx',
61
+ args: [`${ARGENT_PACKAGE_NAME}@${versionSpec}`, 'mcp'],
62
+ env: { ...env, ARGENT_IDLE_TIMEOUT_MINUTES: '0' },
63
+ });
64
+ logger.info(`Waiting for argent tool-server state at ${ARGENT_STATE_FILE}.`);
65
+ const { port: toolServerPort } = await (0, remoteDeviceRunSession_1.waitForFileAsync)({
66
+ filePath: ARGENT_STATE_FILE,
67
+ timeoutMs: STARTUP_TIMEOUT_MS,
68
+ description: 'argent tool-server state',
69
+ parse: parseArgentToolServerState,
70
+ });
71
+ logger.info(`Argent tool-server is listening on port ${toolServerPort}.`);
72
+ logger.info(`Starting cloudflared tunnel to http://localhost:${toolServerPort}.`);
73
+ const cloudflared = (0, remoteDeviceRunSession_1.spawnDetached)({
74
+ command: cloudflaredCommand,
75
+ args: ['tunnel', '--url', `http://localhost:${toolServerPort}`],
76
+ env,
77
+ });
78
+ logger.info('Waiting for a public tunnel URL.');
79
+ const toolsUrl = await (0, remoteDeviceRunSession_1.waitForMatchInOutputAsync)({
80
+ process: cloudflared,
81
+ pattern: /https:\/\/[a-z0-9-]+\.trycloudflare\.com/,
82
+ timeoutMs: STARTUP_TIMEOUT_MS,
83
+ description: 'cloudflared tunnel',
84
+ });
85
+ logger.info(`Tunnel is ready at ${toolsUrl}.`);
86
+ // serve-sim is iOS-only — Android sessions go without a preview URL.
87
+ let webPreviewUrl;
88
+ if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
89
+ const serveSim = await (0, remoteDeviceRunSession_1.startServeSimWithTunnelAsync)({
90
+ env,
91
+ logger,
92
+ timeoutMs: STARTUP_TIMEOUT_MS,
93
+ });
94
+ webPreviewUrl = serveSim.previewUrl;
95
+ logger.info(`Web preview URL: ${webPreviewUrl}`);
96
+ }
97
+ await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
98
+ ctx,
99
+ deviceRunSessionId,
100
+ remoteConfig: {
101
+ toolsUrl,
102
+ ...(webPreviewUrl ? { webPreviewUrl } : {}),
103
+ },
104
+ logger,
105
+ });
106
+ logger.info('Remote session is live. Keeping the job alive until the session is stopped.');
107
+ // Keep the turtle job alive so the tool-server and tunnel stay reachable
108
+ // until stopDeviceRunSession cancels the run.
109
+ await new Promise(() => { });
110
+ },
111
+ });
112
+ }
113
+ function parseArgentToolServerState(raw) {
114
+ const result = ArgentToolServerStateSchema.safeParse(JSON.parse(raw));
115
+ if (!result.success) {
116
+ throw new eas_build_job_1.SystemError(`Expected tool-server state to contain { "port": <number>, ... }: ${result.error.message}`);
117
+ }
118
+ return result.data;
119
+ }
@@ -1,5 +1,5 @@
1
1
  import { bunyan } from '@expo/logger';
2
- import { BuildStepEnv } from '@expo/steps';
2
+ import { BuildRuntimePlatform, BuildStepEnv } from '@expo/steps';
3
3
  import { CustomBuildContext } from '../../customBuildContext';
4
4
  export declare function getDeviceRunSessionIdOrThrow(env: BuildStepEnv): string;
5
5
  export declare function uploadRemoteSessionConfigAsync({ ctx, deviceRunSessionId, remoteConfig, logger, }: {
@@ -30,3 +30,20 @@ export declare function startServeSimWithTunnelAsync({ env, logger, timeoutMs, }
30
30
  previewUrl: string;
31
31
  streamUrl: string;
32
32
  }>;
33
+ export declare function ensureCloudflaredInstalledAsync({ runtimePlatform, env, logger, }: {
34
+ runtimePlatform: BuildRuntimePlatform;
35
+ env: BuildStepEnv;
36
+ logger: bunyan;
37
+ }): Promise<string>;
38
+ export declare function waitForMatchInOutputAsync({ process, pattern, timeoutMs, description, }: {
39
+ process: DetachedProcessHandle;
40
+ pattern: RegExp;
41
+ timeoutMs: number;
42
+ description: string;
43
+ }): Promise<string>;
44
+ export declare function waitForFileAsync<T>({ filePath, timeoutMs, description, parse, }: {
45
+ filePath: string;
46
+ timeoutMs: number;
47
+ description: string;
48
+ parse: (raw: string) => T;
49
+ }): Promise<T>;
@@ -8,11 +8,18 @@ exports.uploadRemoteSessionConfigAsync = uploadRemoteSessionConfigAsync;
8
8
  exports.ensureBrewPackageInstalledAsync = ensureBrewPackageInstalledAsync;
9
9
  exports.spawnDetached = spawnDetached;
10
10
  exports.startServeSimWithTunnelAsync = startServeSimWithTunnelAsync;
11
+ exports.ensureCloudflaredInstalledAsync = ensureCloudflaredInstalledAsync;
12
+ exports.waitForMatchInOutputAsync = waitForMatchInOutputAsync;
13
+ exports.waitForFileAsync = waitForFileAsync;
11
14
  const eas_build_job_1 = require("@expo/eas-build-job");
15
+ const steps_1 = require("@expo/steps");
12
16
  const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
13
17
  const gql_tada_1 = require("gql.tada");
18
+ const node_fs_1 = __importDefault(require("node:fs"));
19
+ const node_os_1 = __importDefault(require("node:os"));
14
20
  const retry_1 = require("../../utils/retry");
15
21
  const TRYCLOUDFLARE_URL_PATTERN = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
22
+ const CLOUDFLARED_LINUX_INSTALL_PATH = '/usr/local/bin/cloudflared';
16
23
  const START_DEVICE_RUN_SESSION_MUTATION = (0, gql_tada_1.graphql)(`
17
24
  mutation StartDeviceRunSession($deviceRunSessionId: ID!, $remoteConfig: JSONObject!) {
18
25
  deviceRunSession {
@@ -100,3 +107,67 @@ function matchLabeledUrl(content, label) {
100
107
  const match = labelPattern.exec(content);
101
108
  return match ? match[1] : null;
102
109
  }
110
+ async function ensureCloudflaredInstalledAsync({ runtimePlatform, env, logger, }) {
111
+ if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
112
+ await ensureBrewPackageInstalledAsync({ name: 'cloudflared', env, logger });
113
+ return 'cloudflared';
114
+ }
115
+ if (await isCommandAvailableAsync({ command: 'cloudflared', env })) {
116
+ return 'cloudflared';
117
+ }
118
+ const cloudflaredArch = cloudflaredLinuxArchForNodeArch(node_os_1.default.arch());
119
+ const downloadUrl = `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cloudflaredArch}`;
120
+ logger.info(`Downloading cloudflared from ${downloadUrl} to ${CLOUDFLARED_LINUX_INSTALL_PATH}.`);
121
+ await (0, turtle_spawn_1.default)('sudo', ['curl', '-fsSL', '-o', CLOUDFLARED_LINUX_INSTALL_PATH, downloadUrl], {
122
+ env,
123
+ logger,
124
+ });
125
+ await (0, turtle_spawn_1.default)('sudo', ['chmod', '+x', CLOUDFLARED_LINUX_INSTALL_PATH], { env, logger });
126
+ // Return the absolute install path so the tunnel command works even when
127
+ // /usr/local/bin is not on the step's PATH.
128
+ return CLOUDFLARED_LINUX_INSTALL_PATH;
129
+ }
130
+ function cloudflaredLinuxArchForNodeArch(arch) {
131
+ if (arch === 'x64') {
132
+ return 'amd64';
133
+ }
134
+ if (arch === 'arm64') {
135
+ return 'arm64';
136
+ }
137
+ throw new eas_build_job_1.SystemError(`Unsupported architecture for cloudflared on Linux: "${arch}". Expected "x64" or "arm64".`);
138
+ }
139
+ async function isCommandAvailableAsync({ command, env, }) {
140
+ try {
141
+ await (0, turtle_spawn_1.default)('bash', ['-c', `command -v ${command}`], { env, ignoreStdio: true });
142
+ return true;
143
+ }
144
+ catch {
145
+ return false;
146
+ }
147
+ }
148
+ async function waitForMatchInOutputAsync({ process, pattern, timeoutMs, description, }) {
149
+ const deadline = Date.now() + timeoutMs;
150
+ while (Date.now() < deadline) {
151
+ const match = pattern.exec(process.getOutput());
152
+ if (match) {
153
+ return match[1] ?? match[0];
154
+ }
155
+ await (0, retry_1.sleepAsync)(1_000);
156
+ }
157
+ throw new eas_build_job_1.SystemError(`Timed out waiting for ${description} to start. Last output:\n${process.getOutput() || '<empty>'}`);
158
+ }
159
+ async function waitForFileAsync({ filePath, timeoutMs, description, parse, }) {
160
+ const deadline = Date.now() + timeoutMs;
161
+ let lastError;
162
+ while (Date.now() < deadline) {
163
+ try {
164
+ const raw = await node_fs_1.default.promises.readFile(filePath, 'utf8');
165
+ return parse(raw);
166
+ }
167
+ catch (err) {
168
+ lastError = err;
169
+ }
170
+ await (0, retry_1.sleepAsync)(1_000);
171
+ }
172
+ throw new eas_build_job_1.SystemError(`Timed out waiting for ${description} to be ready at ${filePath}${lastError instanceof Error ? `: ${lastError.message}` : ''}.`);
173
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/build-tools",
3
- "version": "19.0.3",
3
+ "version": "19.0.6",
4
4
  "bugs": "https://github.com/expo/eas-cli/issues",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Expo <support@expo.io>",
@@ -99,5 +99,5 @@
99
99
  "typescript": "^5.5.4",
100
100
  "uuid": "^9.0.1"
101
101
  },
102
- "gitHead": "503b62b73d2fa1fa40352a93f672fc91f288b602"
102
+ "gitHead": "1e9a0981a5a707543d7686f71540aa5d8f2d098b"
103
103
  }