@expo/build-tools 18.12.0 → 18.12.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/steps/easFunctions.js +2 -0
- package/dist/steps/functions/startAgentDeviceRemoteSession.js +24 -89
- package/dist/steps/functions/startServeSimRemoteSession.d.ts +3 -0
- package/dist/steps/functions/startServeSimRemoteSession.js +72 -0
- package/dist/steps/utils/remoteDeviceRunSession.d.ts +24 -0
- package/dist/steps/utils/remoteDeviceRunSession.js +65 -0
- package/package.json +2 -2
|
@@ -41,6 +41,7 @@ const startAgentDeviceRemoteSession_1 = require("./functions/startAgentDeviceRem
|
|
|
41
41
|
const startAndroidEmulator_1 = require("./functions/startAndroidEmulator");
|
|
42
42
|
const startCuttlefishDevice_1 = require("./functions/startCuttlefishDevice");
|
|
43
43
|
const startIosSimulator_1 = require("./functions/startIosSimulator");
|
|
44
|
+
const startServeSimRemoteSession_1 = require("./functions/startServeSimRemoteSession");
|
|
44
45
|
const uploadArtifact_1 = require("./functions/uploadArtifact");
|
|
45
46
|
const uploadToAsc_1 = require("./functions/uploadToAsc");
|
|
46
47
|
const useNpmToken_1 = require("./functions/useNpmToken");
|
|
@@ -79,6 +80,7 @@ function getEasFunctions(ctx) {
|
|
|
79
80
|
(0, startAndroidEmulator_1.createStartAndroidEmulatorBuildFunction)(),
|
|
80
81
|
(0, startCuttlefishDevice_1.createStartCuttlefishDeviceBuildFunction)(),
|
|
81
82
|
(0, startIosSimulator_1.createStartIosSimulatorBuildFunction)(),
|
|
83
|
+
(0, startServeSimRemoteSession_1.createStartServeSimRemoteSessionBuildFunction)(ctx),
|
|
82
84
|
(0, installMaestro_1.createInstallMaestroBuildFunction)(),
|
|
83
85
|
(0, installPods_1.createInstallPodsBuildFunction)(),
|
|
84
86
|
(0, sendSlackMessage_1.createSendSlackMessageFunction)(),
|
|
@@ -4,37 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createStartAgentDeviceRemoteSessionBuildFunction = createStartAgentDeviceRemoteSessionBuildFunction;
|
|
7
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
7
8
|
const steps_1 = require("@expo/steps");
|
|
8
9
|
const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
|
|
9
|
-
const gql_tada_1 = require("gql.tada");
|
|
10
|
-
const node_child_process_1 = __importDefault(require("node:child_process"));
|
|
11
10
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
11
|
const node_os_1 = __importDefault(require("node:os"));
|
|
13
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
14
13
|
const retry_1 = require("../../utils/retry");
|
|
15
|
-
const
|
|
14
|
+
const remoteDeviceRunSession_1 = require("../utils/remoteDeviceRunSession");
|
|
16
15
|
const AGENT_DEVICE_REPO_URL = 'https://github.com/callstackincubator/agent-device.git';
|
|
17
16
|
const SRC_DIR = '/tmp/agent-device-src';
|
|
18
|
-
const RUN_DIR = '/tmp/agent-device';
|
|
19
|
-
const DAEMON_LOG = node_path_1.default.join(RUN_DIR, 'daemon.log');
|
|
20
|
-
const TUNNEL_LOG = node_path_1.default.join(RUN_DIR, 'cloudflared.log');
|
|
21
17
|
const DAEMON_JSON_PATH = node_path_1.default.join(node_os_1.default.homedir(), '.agent-device', 'daemon.json');
|
|
22
18
|
const XCODE_DEVELOPER_DIR = '/Applications/Xcode.app/Contents/Developer';
|
|
23
19
|
const CLOUDFLARED_LINUX_INSTALL_PATH = '/usr/local/bin/cloudflared';
|
|
24
20
|
const STARTUP_TIMEOUT_MS = 60_000;
|
|
25
|
-
const START_DEVICE_RUN_SESSION_MUTATION = (0, gql_tada_1.graphql)(`
|
|
26
|
-
mutation StartDeviceRunSession($deviceRunSessionId: ID!, $remoteConfig: JSONObject!) {
|
|
27
|
-
deviceRunSession {
|
|
28
|
-
startDeviceRunSession(
|
|
29
|
-
deviceRunSessionId: $deviceRunSessionId
|
|
30
|
-
remoteConfig: $remoteConfig
|
|
31
|
-
) {
|
|
32
|
-
id
|
|
33
|
-
status
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
`);
|
|
38
21
|
function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
39
22
|
return new steps_1.BuildFunction({
|
|
40
23
|
namespace: 'eas',
|
|
@@ -52,17 +35,10 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
|
52
35
|
// Fail fast before any expensive setup if the orchestrator-injected
|
|
53
36
|
// DEVICE_RUN_SESSION_ID env var is missing — without it we cannot
|
|
54
37
|
// report the remote config back to the API server.
|
|
55
|
-
const deviceRunSessionId = env
|
|
56
|
-
if (!deviceRunSessionId) {
|
|
57
|
-
throw new eas_build_job_1.SystemError('DEVICE_RUN_SESSION_ID is not set. ' +
|
|
58
|
-
'This step must run as part of a device run session created by the API server, ' +
|
|
59
|
-
'which injects DEVICE_RUN_SESSION_ID into the job environment.');
|
|
60
|
-
}
|
|
38
|
+
const deviceRunSessionId = (0, remoteDeviceRunSession_1.getDeviceRunSessionIdOrThrow)(env);
|
|
61
39
|
const packageVersion = inputs.package_version.value;
|
|
62
40
|
const { runtimePlatform } = global;
|
|
63
41
|
logger.info(`Starting agent-device remote session (version: ${packageVersion ?? 'latest'}, runtime: ${runtimePlatform}).`);
|
|
64
|
-
logger.info(`Preparing runtime directory at ${RUN_DIR}.`);
|
|
65
|
-
await node_fs_1.default.promises.mkdir(RUN_DIR, { recursive: true });
|
|
66
42
|
if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
|
|
67
43
|
logger.info(`Selecting Xcode developer directory: ${XCODE_DEVELOPER_DIR}.`);
|
|
68
44
|
await (0, turtle_spawn_1.default)('sudo', ['xcode-select', '-s', XCODE_DEVELOPER_DIR], { env, logger });
|
|
@@ -83,13 +59,12 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
|
83
59
|
env,
|
|
84
60
|
logger,
|
|
85
61
|
});
|
|
86
|
-
logger.info(
|
|
87
|
-
|
|
88
|
-
command: 'bun
|
|
62
|
+
logger.info('Launching agent-device daemon.');
|
|
63
|
+
(0, remoteDeviceRunSession_1.spawnDetached)({
|
|
64
|
+
command: 'bun',
|
|
65
|
+
args: ['run', 'src/daemon.ts'],
|
|
89
66
|
cwd: SRC_DIR,
|
|
90
|
-
logFile: DAEMON_LOG,
|
|
91
67
|
env: { ...env, AGENT_DEVICE_DAEMON_SERVER_MODE: 'http' },
|
|
92
|
-
logger,
|
|
93
68
|
});
|
|
94
69
|
logger.info(`Waiting for daemon credentials at ${DAEMON_JSON_PATH}.`);
|
|
95
70
|
await waitForFileAsync({
|
|
@@ -99,31 +74,26 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
|
99
74
|
});
|
|
100
75
|
const { port: daemonPort, token: daemonToken } = readDaemonInfo(DAEMON_JSON_PATH);
|
|
101
76
|
logger.info(`Daemon is listening on port ${daemonPort}; loaded auth token.`);
|
|
102
|
-
logger.info(`Starting cloudflared tunnel to http://localhost:${daemonPort}
|
|
103
|
-
|
|
104
|
-
command:
|
|
105
|
-
|
|
77
|
+
logger.info(`Starting cloudflared tunnel to http://localhost:${daemonPort}.`);
|
|
78
|
+
const cloudflared = (0, remoteDeviceRunSession_1.spawnDetached)({
|
|
79
|
+
command: cloudflaredCommand,
|
|
80
|
+
args: ['tunnel', '--url', `http://localhost:${daemonPort}`],
|
|
106
81
|
env,
|
|
107
|
-
logger,
|
|
108
82
|
});
|
|
109
83
|
logger.info('Waiting for a public tunnel URL.');
|
|
110
|
-
const tunnelUrl = await
|
|
111
|
-
|
|
84
|
+
const tunnelUrl = await waitForMatchInOutputAsync({
|
|
85
|
+
process: cloudflared,
|
|
112
86
|
pattern: /https:\/\/[a-z0-9-]+\.trycloudflare\.com/,
|
|
113
87
|
timeoutMs: STARTUP_TIMEOUT_MS,
|
|
114
88
|
description: 'cloudflared tunnel',
|
|
115
89
|
});
|
|
116
90
|
logger.info(`Tunnel is ready at ${tunnelUrl}.`);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
.mutation(START_DEVICE_RUN_SESSION_MUTATION, {
|
|
91
|
+
await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
|
|
92
|
+
ctx,
|
|
120
93
|
deviceRunSessionId,
|
|
121
94
|
remoteConfig: { url: tunnelUrl, token: daemonToken },
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (result.error) {
|
|
125
|
-
throw new eas_build_job_1.SystemError(`Failed to start device run session ${deviceRunSessionId}: ${result.error.message}`);
|
|
126
|
-
}
|
|
95
|
+
logger,
|
|
96
|
+
});
|
|
127
97
|
logger.info('Remote session is live. Keeping the job alive until the session is stopped.');
|
|
128
98
|
// Keep the turtle job alive so the daemon and tunnel stay reachable
|
|
129
99
|
// until stopDeviceRunSession cancels the run.
|
|
@@ -133,7 +103,7 @@ function createStartAgentDeviceRemoteSessionBuildFunction(ctx) {
|
|
|
133
103
|
}
|
|
134
104
|
async function ensureCloudflaredInstalledAsync({ runtimePlatform, env, logger, }) {
|
|
135
105
|
if (runtimePlatform === steps_1.BuildRuntimePlatform.DARWIN) {
|
|
136
|
-
await ensureBrewPackageInstalledAsync({ name: 'cloudflared', env, logger });
|
|
106
|
+
await (0, remoteDeviceRunSession_1.ensureBrewPackageInstalledAsync)({ name: 'cloudflared', env, logger });
|
|
137
107
|
return 'cloudflared';
|
|
138
108
|
}
|
|
139
109
|
if (await isCommandAvailableAsync({ command: 'cloudflared', env })) {
|
|
@@ -158,10 +128,7 @@ function cloudflaredLinuxArchForNodeArch(arch) {
|
|
|
158
128
|
if (arch === 'arm64') {
|
|
159
129
|
return 'arm64';
|
|
160
130
|
}
|
|
161
|
-
throw new
|
|
162
|
-
}
|
|
163
|
-
async function ensureBrewPackageInstalledAsync({ name, env, logger, }) {
|
|
164
|
-
await (0, turtle_spawn_1.default)('bash', ['-c', `command -v ${name} >/dev/null 2>&1 || HOMEBREW_NO_AUTO_UPDATE=1 brew install ${name}`], { env, logger });
|
|
131
|
+
throw new eas_build_job_1.SystemError(`Unsupported architecture for cloudflared on Linux: "${arch}". Expected "x64" or "arm64".`);
|
|
165
132
|
}
|
|
166
133
|
async function isCommandAvailableAsync({ command, env, }) {
|
|
167
134
|
try {
|
|
@@ -179,40 +146,16 @@ async function cloneAgentDeviceAsync({ packageVersion, env, logger, }) {
|
|
|
179
146
|
logger,
|
|
180
147
|
});
|
|
181
148
|
}
|
|
182
|
-
async function
|
|
183
|
-
// Launch the process fully detached so this function returns immediately and
|
|
184
|
-
// the grandchild survives the step. Stdio goes to a log file so the daemon
|
|
185
|
-
// output can be polled, and we unref so Node doesn't wait on it.
|
|
186
|
-
const fd = node_fs_1.default.openSync(logFile, 'a');
|
|
187
|
-
try {
|
|
188
|
-
const child = node_child_process_1.default.spawn('bash', ['-c', command], {
|
|
189
|
-
cwd,
|
|
190
|
-
env,
|
|
191
|
-
detached: true,
|
|
192
|
-
stdio: ['ignore', fd, fd],
|
|
193
|
-
});
|
|
194
|
-
if (!child.pid) {
|
|
195
|
-
throw new Error(`Failed to spawn detached process: ${command}`);
|
|
196
|
-
}
|
|
197
|
-
child.unref();
|
|
198
|
-
logger.info(`Started detached process (pid ${child.pid}).`);
|
|
199
|
-
}
|
|
200
|
-
finally {
|
|
201
|
-
node_fs_1.default.closeSync(fd);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
async function waitForMatchInLogAsync({ logFile, pattern, timeoutMs, description, }) {
|
|
149
|
+
async function waitForMatchInOutputAsync({ process, pattern, timeoutMs, description, }) {
|
|
205
150
|
const deadline = Date.now() + timeoutMs;
|
|
206
151
|
while (Date.now() < deadline) {
|
|
207
|
-
const
|
|
208
|
-
const match = pattern.exec(content);
|
|
152
|
+
const match = pattern.exec(process.getOutput());
|
|
209
153
|
if (match) {
|
|
210
154
|
return match[1] ?? match[0];
|
|
211
155
|
}
|
|
212
156
|
await (0, retry_1.sleepAsync)(1_000);
|
|
213
157
|
}
|
|
214
|
-
|
|
215
|
-
throw new Error(`Timed out waiting for ${description} to start. Last log contents:\n${tail || '<empty>'}`);
|
|
158
|
+
throw new eas_build_job_1.SystemError(`Timed out waiting for ${description} to start. Last output:\n${process.getOutput() || '<empty>'}`);
|
|
216
159
|
}
|
|
217
160
|
async function waitForFileAsync({ filePath, timeoutMs, description, }) {
|
|
218
161
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -226,15 +169,7 @@ async function waitForFileAsync({ filePath, timeoutMs, description, }) {
|
|
|
226
169
|
}
|
|
227
170
|
await (0, retry_1.sleepAsync)(1_000);
|
|
228
171
|
}
|
|
229
|
-
throw new
|
|
230
|
-
}
|
|
231
|
-
async function readFileOrEmptyAsync(filePath) {
|
|
232
|
-
try {
|
|
233
|
-
return await node_fs_1.default.promises.readFile(filePath, 'utf8');
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
return '';
|
|
237
|
-
}
|
|
172
|
+
throw new eas_build_job_1.SystemError(`Timed out waiting for ${description} to write ${filePath}.`);
|
|
238
173
|
}
|
|
239
174
|
function readDaemonInfo(filePath) {
|
|
240
175
|
const raw = node_fs_1.default.readFileSync(filePath, 'utf8');
|
|
@@ -243,7 +178,7 @@ function readDaemonInfo(filePath) {
|
|
|
243
178
|
typeof parsed !== 'object' ||
|
|
244
179
|
typeof parsed.httpPort !== 'number' ||
|
|
245
180
|
typeof parsed.token !== 'string') {
|
|
246
|
-
throw new
|
|
181
|
+
throw new eas_build_job_1.SystemError(`Expected ${filePath} to contain { "httpPort": <number>, "token": "..." }.`);
|
|
247
182
|
}
|
|
248
183
|
const { httpPort, token } = parsed;
|
|
249
184
|
return { port: httpPort, token };
|
|
@@ -0,0 +1,72 @@
|
|
|
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.createStartServeSimRemoteSessionBuildFunction = createStartServeSimRemoteSessionBuildFunction;
|
|
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 retry_1 = require("../../utils/retry");
|
|
11
|
+
const remoteDeviceRunSession_1 = require("../utils/remoteDeviceRunSession");
|
|
12
|
+
const XCODE_DEVELOPER_DIR = '/Applications/Xcode.app/Contents/Developer';
|
|
13
|
+
const STARTUP_TIMEOUT_MS = 60_000;
|
|
14
|
+
const TRYCLOUDFLARE_URL_PATTERN = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
|
|
15
|
+
function createStartServeSimRemoteSessionBuildFunction(ctx) {
|
|
16
|
+
return new steps_1.BuildFunction({
|
|
17
|
+
namespace: 'eas',
|
|
18
|
+
id: 'start_serve_sim_remote_session',
|
|
19
|
+
name: 'Start serve-sim remote session',
|
|
20
|
+
__metricsId: 'eas/start_serve_sim_remote_session',
|
|
21
|
+
supportedRuntimePlatforms: [steps_1.BuildRuntimePlatform.DARWIN],
|
|
22
|
+
fn: async ({ logger }, { env }) => {
|
|
23
|
+
const deviceRunSessionId = (0, remoteDeviceRunSession_1.getDeviceRunSessionIdOrThrow)(env);
|
|
24
|
+
logger.info('Starting serve-sim remote session.');
|
|
25
|
+
logger.info(`Selecting Xcode developer directory: ${XCODE_DEVELOPER_DIR}.`);
|
|
26
|
+
await (0, turtle_spawn_1.default)('sudo', ['xcode-select', '-s', XCODE_DEVELOPER_DIR], { env, logger });
|
|
27
|
+
logger.info('Ensuring cloudflared is installed.');
|
|
28
|
+
await (0, remoteDeviceRunSession_1.ensureBrewPackageInstalledAsync)({ name: 'cloudflared', env, logger });
|
|
29
|
+
logger.info('Launching serve-sim with tunnel.');
|
|
30
|
+
const serveSim = (0, remoteDeviceRunSession_1.spawnDetached)({
|
|
31
|
+
command: 'npx',
|
|
32
|
+
args: ['serve-sim-szdziedzic@latest', '--tunnel'],
|
|
33
|
+
env,
|
|
34
|
+
});
|
|
35
|
+
logger.info('Waiting for serve-sim to report tunnel and stream URLs.');
|
|
36
|
+
const { previewUrl, streamUrl } = await waitForServeSimUrlsAsync({
|
|
37
|
+
serveSim,
|
|
38
|
+
timeoutMs: STARTUP_TIMEOUT_MS,
|
|
39
|
+
});
|
|
40
|
+
logger.info(`Preview URL: ${previewUrl}`);
|
|
41
|
+
logger.info(`Stream URL: ${streamUrl}`);
|
|
42
|
+
await (0, remoteDeviceRunSession_1.uploadRemoteSessionConfigAsync)({
|
|
43
|
+
ctx,
|
|
44
|
+
deviceRunSessionId,
|
|
45
|
+
remoteConfig: { previewUrl, streamUrl },
|
|
46
|
+
logger,
|
|
47
|
+
});
|
|
48
|
+
logger.info('Remote session is live. Keeping the job alive until the session is stopped.');
|
|
49
|
+
// Keep the turtle job alive so the serve-sim tunnel stays reachable
|
|
50
|
+
// until stopDeviceRunSession cancels the run.
|
|
51
|
+
await new Promise(() => { });
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function waitForServeSimUrlsAsync({ serveSim, timeoutMs, }) {
|
|
56
|
+
const deadline = Date.now() + timeoutMs;
|
|
57
|
+
while (Date.now() < deadline) {
|
|
58
|
+
const output = serveSim.getOutput();
|
|
59
|
+
const previewUrl = matchLabeledUrl(output, 'Tunnel');
|
|
60
|
+
const streamUrl = matchLabeledUrl(output, 'Stream');
|
|
61
|
+
if (previewUrl && streamUrl) {
|
|
62
|
+
return { previewUrl, streamUrl };
|
|
63
|
+
}
|
|
64
|
+
await (0, retry_1.sleepAsync)(1_000);
|
|
65
|
+
}
|
|
66
|
+
throw new eas_build_job_1.SystemError(`Timed out waiting for serve-sim to report Tunnel and Stream URLs. Last output:\n${serveSim.getOutput() || '<empty>'}`);
|
|
67
|
+
}
|
|
68
|
+
function matchLabeledUrl(content, label) {
|
|
69
|
+
const labelPattern = new RegExp(`${label}:\\s*(${TRYCLOUDFLARE_URL_PATTERN.source})`);
|
|
70
|
+
const match = labelPattern.exec(content);
|
|
71
|
+
return match ? match[1] : null;
|
|
72
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { bunyan } from '@expo/logger';
|
|
2
|
+
import { BuildStepEnv } from '@expo/steps';
|
|
3
|
+
import { CustomBuildContext } from '../../customBuildContext';
|
|
4
|
+
export declare function getDeviceRunSessionIdOrThrow(env: BuildStepEnv): string;
|
|
5
|
+
export declare function uploadRemoteSessionConfigAsync({ ctx, deviceRunSessionId, remoteConfig, logger, }: {
|
|
6
|
+
ctx: CustomBuildContext;
|
|
7
|
+
deviceRunSessionId: string;
|
|
8
|
+
remoteConfig: Record<string, unknown>;
|
|
9
|
+
logger: bunyan;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
export declare function ensureBrewPackageInstalledAsync({ name, env, logger, }: {
|
|
12
|
+
name: string;
|
|
13
|
+
env: BuildStepEnv;
|
|
14
|
+
logger: bunyan;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
export type DetachedProcessHandle = {
|
|
17
|
+
getOutput: () => string;
|
|
18
|
+
};
|
|
19
|
+
export declare function spawnDetached({ command, args, cwd, env, }: {
|
|
20
|
+
command: string;
|
|
21
|
+
args: string[];
|
|
22
|
+
cwd?: string;
|
|
23
|
+
env: BuildStepEnv;
|
|
24
|
+
}): DetachedProcessHandle;
|
|
@@ -0,0 +1,65 @@
|
|
|
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.getDeviceRunSessionIdOrThrow = getDeviceRunSessionIdOrThrow;
|
|
7
|
+
exports.uploadRemoteSessionConfigAsync = uploadRemoteSessionConfigAsync;
|
|
8
|
+
exports.ensureBrewPackageInstalledAsync = ensureBrewPackageInstalledAsync;
|
|
9
|
+
exports.spawnDetached = spawnDetached;
|
|
10
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
11
|
+
const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
|
|
12
|
+
const gql_tada_1 = require("gql.tada");
|
|
13
|
+
const START_DEVICE_RUN_SESSION_MUTATION = (0, gql_tada_1.graphql)(`
|
|
14
|
+
mutation StartDeviceRunSession($deviceRunSessionId: ID!, $remoteConfig: JSONObject!) {
|
|
15
|
+
deviceRunSession {
|
|
16
|
+
startDeviceRunSession(
|
|
17
|
+
deviceRunSessionId: $deviceRunSessionId
|
|
18
|
+
remoteConfig: $remoteConfig
|
|
19
|
+
) {
|
|
20
|
+
id
|
|
21
|
+
status
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
`);
|
|
26
|
+
function getDeviceRunSessionIdOrThrow(env) {
|
|
27
|
+
const deviceRunSessionId = env.DEVICE_RUN_SESSION_ID;
|
|
28
|
+
if (!deviceRunSessionId) {
|
|
29
|
+
throw new eas_build_job_1.SystemError('DEVICE_RUN_SESSION_ID is not set. ' +
|
|
30
|
+
'This step must run as part of a device run session created by the API server, ' +
|
|
31
|
+
'which injects DEVICE_RUN_SESSION_ID into the job environment.');
|
|
32
|
+
}
|
|
33
|
+
return deviceRunSessionId;
|
|
34
|
+
}
|
|
35
|
+
async function uploadRemoteSessionConfigAsync({ ctx, deviceRunSessionId, remoteConfig, logger, }) {
|
|
36
|
+
logger.info(`Reporting remote config to the API server (device run session: ${deviceRunSessionId}).`);
|
|
37
|
+
const result = await ctx.graphqlClient
|
|
38
|
+
.mutation(START_DEVICE_RUN_SESSION_MUTATION, { deviceRunSessionId, remoteConfig })
|
|
39
|
+
.toPromise();
|
|
40
|
+
if (result.error) {
|
|
41
|
+
throw new eas_build_job_1.SystemError(`Failed to start device run session ${deviceRunSessionId}: ${result.error.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function ensureBrewPackageInstalledAsync({ name, env, logger, }) {
|
|
45
|
+
await (0, turtle_spawn_1.default)('bash', ['-c', `command -v ${name} >/dev/null 2>&1 || HOMEBREW_NO_AUTO_UPDATE=1 brew install ${name}`], { env, logger });
|
|
46
|
+
}
|
|
47
|
+
function spawnDetached({ command, args, cwd, env, }) {
|
|
48
|
+
const promise = (0, turtle_spawn_1.default)(command, args, {
|
|
49
|
+
cwd,
|
|
50
|
+
env,
|
|
51
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
52
|
+
detached: true,
|
|
53
|
+
});
|
|
54
|
+
// We don't await the process — it should outlive this step. Failures show
|
|
55
|
+
// up in the captured output; suppress unhandled rejections here.
|
|
56
|
+
promise.catch(() => { });
|
|
57
|
+
promise.child.unref();
|
|
58
|
+
let output = '';
|
|
59
|
+
const appendChunk = (chunk) => {
|
|
60
|
+
output += chunk.toString();
|
|
61
|
+
};
|
|
62
|
+
promise.child.stdout?.on('data', appendChunk);
|
|
63
|
+
promise.child.stderr?.on('data', appendChunk);
|
|
64
|
+
return { getOutput: () => output };
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/build-tools",
|
|
3
|
-
"version": "18.12.
|
|
3
|
+
"version": "18.12.1",
|
|
4
4
|
"bugs": "https://github.com/expo/eas-cli/issues",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Expo <support@expo.io>",
|
|
@@ -98,5 +98,5 @@
|
|
|
98
98
|
"typescript": "^5.5.4",
|
|
99
99
|
"uuid": "^9.0.1"
|
|
100
100
|
},
|
|
101
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "f46b469267f4f87be6a6d1acff22464c8cf89b26"
|
|
102
102
|
}
|