@expo/build-tools 24.2.0 → 24.3.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.
|
@@ -52,6 +52,7 @@ const startIosSimulatorRecordings_1 = require("./functions/startIosSimulatorReco
|
|
|
52
52
|
const startLocalEgress_1 = require("./functions/startLocalEgress");
|
|
53
53
|
const startWebPreviewRemoteSession_1 = require("./functions/startWebPreviewRemoteSession");
|
|
54
54
|
const startServeSimMetrics_1 = require("./functions/startServeSimMetrics");
|
|
55
|
+
const startSandbox_1 = require("./functions/startSandbox");
|
|
55
56
|
const collectServeSimMetrics_1 = require("./functions/collectServeSimMetrics");
|
|
56
57
|
const uploadArtifact_1 = require("./functions/uploadArtifact");
|
|
57
58
|
const uploadDeviceRunSessionScreenRecordings_1 = require("./functions/uploadDeviceRunSessionScreenRecordings");
|
|
@@ -106,6 +107,7 @@ function getEasFunctions(ctx) {
|
|
|
106
107
|
(0, uploadDeviceRunSessionScreenRecordings_1.createUploadDeviceRunSessionScreenRecordingsBuildFunction)(ctx),
|
|
107
108
|
(0, startWebPreviewRemoteSession_1.createStartWebPreviewRemoteSessionBuildFunction)(ctx),
|
|
108
109
|
(0, startServeSimMetrics_1.createStartServeSimMetricsBuildFunction)(),
|
|
110
|
+
(0, startSandbox_1.createStartSandboxBuildFunction)(ctx),
|
|
109
111
|
(0, collectServeSimMetrics_1.createCollectServeSimMetricsBuildFunction)(ctx),
|
|
110
112
|
(0, installMaestro_1.createInstallMaestroBuildFunction)(),
|
|
111
113
|
(0, installPods_1.createInstallPodsBuildFunction)(),
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { BuildFunction } from '@expo/steps';
|
|
2
|
+
import { CustomBuildContext } from '../../customBuildContext';
|
|
3
|
+
export declare function createStartSandboxBuildFunction(ctx: CustomBuildContext): BuildFunction;
|
|
4
|
+
export declare function markSandboxReadyAsync(ctx: CustomBuildContext, sandboxId: string, signal?: AbortSignal): Promise<void>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createStartSandboxBuildFunction = createStartSandboxBuildFunction;
|
|
4
|
+
exports.markSandboxReadyAsync = markSandboxReadyAsync;
|
|
5
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
6
|
+
const steps_1 = require("@expo/steps");
|
|
7
|
+
const gql_tada_1 = require("gql.tada");
|
|
8
|
+
const logPhase_1 = require("../../utils/logPhase");
|
|
9
|
+
const sandboxDaemon_1 = require("../utils/sandboxDaemon");
|
|
10
|
+
const MARK_SANDBOX_READY_MUTATION = (0, gql_tada_1.graphql)(`
|
|
11
|
+
mutation MarkSandboxReady($sandboxId: ID!) {
|
|
12
|
+
sandbox {
|
|
13
|
+
markSandboxReady(sandboxId: $sandboxId) {
|
|
14
|
+
id
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
`);
|
|
19
|
+
const RECONNECT_DELAY_MS = 1_000;
|
|
20
|
+
const AWAIT_SANDBOX_STEP_NAME = 'Await sandbox completion';
|
|
21
|
+
function createStartSandboxBuildFunction(ctx) {
|
|
22
|
+
return new steps_1.BuildFunction({
|
|
23
|
+
namespace: 'eas',
|
|
24
|
+
id: 'start_sandbox',
|
|
25
|
+
name: 'Start sandbox daemon',
|
|
26
|
+
__metricsId: 'eas/start_sandbox',
|
|
27
|
+
inputProviders: [
|
|
28
|
+
steps_1.BuildStepInput.createProvider({
|
|
29
|
+
id: 'sandbox_id',
|
|
30
|
+
required: true,
|
|
31
|
+
allowedValueTypeName: steps_1.BuildStepInputValueTypeName.STRING,
|
|
32
|
+
}),
|
|
33
|
+
],
|
|
34
|
+
fn: async (stepCtx, { inputs, signal }) => {
|
|
35
|
+
const sandboxToken = ctx.env.__EAS_SANDBOX_MCP_TOKEN;
|
|
36
|
+
if (!sandboxToken) {
|
|
37
|
+
throw new eas_build_job_1.SystemError('__EAS_SANDBOX_MCP_TOKEN is required to start the sandbox daemon.');
|
|
38
|
+
}
|
|
39
|
+
const mcpServerUrl = ctx.mcpServerUrl;
|
|
40
|
+
if (!mcpServerUrl) {
|
|
41
|
+
throw new eas_build_job_1.SystemError('MCP server URL is required to start the sandbox daemon.');
|
|
42
|
+
}
|
|
43
|
+
const sandboxId = String(inputs.sandbox_id.value);
|
|
44
|
+
const daemon = await (0, sandboxDaemon_1.startSandboxDaemonAsync)({
|
|
45
|
+
credential: sandboxToken,
|
|
46
|
+
serverUrl: mcpServerUrl,
|
|
47
|
+
reconnectDelayMs: RECONNECT_DELAY_MS,
|
|
48
|
+
logger: stepCtx.logger,
|
|
49
|
+
signal,
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
await daemon.ready;
|
|
53
|
+
signal?.throwIfAborted();
|
|
54
|
+
await markSandboxReadyAsync(ctx, sandboxId, signal);
|
|
55
|
+
signal?.throwIfAborted();
|
|
56
|
+
stepCtx.logger.info('Sandbox daemon started.');
|
|
57
|
+
await (0, logPhase_1.withLogPhaseAsync)(stepCtx.logger, AWAIT_SANDBOX_STEP_NAME, async () => {
|
|
58
|
+
await waitForCancellationAsync(signal);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await daemon.stopAsync();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async function markSandboxReadyAsync(ctx, sandboxId, signal) {
|
|
68
|
+
signal?.throwIfAborted();
|
|
69
|
+
const result = await ctx.graphqlClient
|
|
70
|
+
.mutation(MARK_SANDBOX_READY_MUTATION, { sandboxId }, signal
|
|
71
|
+
? {
|
|
72
|
+
fetch: (input, init) => fetch(input, {
|
|
73
|
+
...init,
|
|
74
|
+
signal: init?.signal ? AbortSignal.any([signal, init.signal]) : signal,
|
|
75
|
+
}),
|
|
76
|
+
}
|
|
77
|
+
: undefined)
|
|
78
|
+
.toPromise();
|
|
79
|
+
signal?.throwIfAborted();
|
|
80
|
+
if (result.error) {
|
|
81
|
+
throw new eas_build_job_1.SystemError(`Failed to mark sandbox ${sandboxId} as ready.`, {
|
|
82
|
+
cause: result.error,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function waitForCancellationAsync(signal) {
|
|
87
|
+
if (signal?.aborted) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
await new Promise(resolve => {
|
|
91
|
+
signal?.addEventListener('abort', () => resolve(), { once: true });
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type bunyan } from '@expo/logger';
|
|
2
|
+
export interface SandboxDaemonOptions {
|
|
3
|
+
credential: string;
|
|
4
|
+
serverUrl: string;
|
|
5
|
+
reconnectDelayMs: number;
|
|
6
|
+
logger: bunyan;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
export interface SandboxDaemon {
|
|
10
|
+
ready: Promise<void>;
|
|
11
|
+
stopAsync(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function startSandboxDaemonAsync(options: SandboxDaemonOptions): Promise<SandboxDaemon>;
|
|
@@ -0,0 +1,90 @@
|
|
|
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.startSandboxDaemonAsync = startSandboxDaemonAsync;
|
|
7
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
8
|
+
const promises_1 = require("node:timers/promises");
|
|
9
|
+
const ws_1 = __importDefault(require("ws"));
|
|
10
|
+
async function startSandboxDaemonAsync(options) {
|
|
11
|
+
options.signal?.throwIfAborted();
|
|
12
|
+
let socket;
|
|
13
|
+
const abortController = new AbortController();
|
|
14
|
+
let hasConnected = false;
|
|
15
|
+
let resolveConnected;
|
|
16
|
+
let rejectConnected;
|
|
17
|
+
const connected = new Promise((resolve, reject) => {
|
|
18
|
+
resolveConnected = resolve;
|
|
19
|
+
rejectConnected = reject;
|
|
20
|
+
});
|
|
21
|
+
const stop = () => {
|
|
22
|
+
abortController.abort();
|
|
23
|
+
socket?.close(1000, 'sandbox stopped');
|
|
24
|
+
};
|
|
25
|
+
options.signal?.addEventListener('abort', stop, { once: true });
|
|
26
|
+
const connectionLoop = (async () => {
|
|
27
|
+
while (!abortController.signal.aborted) {
|
|
28
|
+
try {
|
|
29
|
+
socket = new ws_1.default(new URL('/sandbox/connect', options.serverUrl), {
|
|
30
|
+
handshakeTimeout: 10_000,
|
|
31
|
+
headers: { Authorization: `Bearer ${options.credential}` },
|
|
32
|
+
});
|
|
33
|
+
await waitForOpen(socket);
|
|
34
|
+
options.logger.info('Sandbox MCP server connected.');
|
|
35
|
+
hasConnected = true;
|
|
36
|
+
resolveConnected();
|
|
37
|
+
await waitForClose(socket);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (!hasConnected) {
|
|
41
|
+
const message = `Sandbox MCP server connection failed: ${error?.message ?? 'unknown error'}`;
|
|
42
|
+
if (!abortController.signal.aborted) {
|
|
43
|
+
options.logger.error({ err: error }, message);
|
|
44
|
+
}
|
|
45
|
+
rejectConnected(new eas_build_job_1.SystemError(message, { cause: error }));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!abortController.signal.aborted) {
|
|
49
|
+
options.logger.warn({ err: error }, `Sandbox MCP server connection failed: ${error?.message ?? 'unknown error'}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!abortController.signal.aborted) {
|
|
53
|
+
try {
|
|
54
|
+
await (0, promises_1.setTimeout)(options.reconnectDelayMs, undefined, {
|
|
55
|
+
signal: abortController.signal,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (!abortController.signal.aborted) {
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
})();
|
|
66
|
+
return {
|
|
67
|
+
ready: connected,
|
|
68
|
+
async stopAsync() {
|
|
69
|
+
options.signal?.removeEventListener('abort', stop);
|
|
70
|
+
stop();
|
|
71
|
+
await connectionLoop;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function waitForOpen(socket) {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
socket.once('open', resolve);
|
|
78
|
+
socket.once('error', reject);
|
|
79
|
+
socket.once('close', () => reject(new Error('Sandbox MCP server closed before it connected.')));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function waitForClose(socket) {
|
|
83
|
+
if (socket.readyState === ws_1.default.CLOSING || socket.readyState === ws_1.default.CLOSED) {
|
|
84
|
+
return Promise.resolve();
|
|
85
|
+
}
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
socket.once('close', resolve);
|
|
88
|
+
socket.once('error', reject);
|
|
89
|
+
});
|
|
90
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/build-tools",
|
|
3
|
-
"version": "24.
|
|
3
|
+
"version": "24.3.0",
|
|
4
4
|
"bugs": "https://github.com/expo/eas-cli/issues",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Expo <support@expo.io>",
|
|
@@ -40,14 +40,14 @@
|
|
|
40
40
|
"@expo/config": "55.0.10",
|
|
41
41
|
"@expo/config-plugins": "55.0.7",
|
|
42
42
|
"@expo/downloader": "24.0.0",
|
|
43
|
-
"@expo/eas-build-job": "24.
|
|
43
|
+
"@expo/eas-build-job": "24.3.0",
|
|
44
44
|
"@expo/env": "^0.4.0",
|
|
45
45
|
"@expo/logger": "24.0.0",
|
|
46
46
|
"@expo/package-manager": "1.9.10",
|
|
47
47
|
"@expo/plist": "^0.3.5",
|
|
48
48
|
"@expo/results": "^1.0.0",
|
|
49
49
|
"@expo/spawn-async": "1.7.2",
|
|
50
|
-
"@expo/steps": "24.
|
|
50
|
+
"@expo/steps": "24.3.0",
|
|
51
51
|
"@expo/template-file": "24.0.0",
|
|
52
52
|
"@expo/turtle-spawn": "24.0.0",
|
|
53
53
|
"@expo/xcpretty": "^4.3.1",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"semver": "^7.6.2",
|
|
76
76
|
"tar": "7.5.19",
|
|
77
77
|
"uuid": "9.0.1",
|
|
78
|
+
"ws": "8.21.1",
|
|
78
79
|
"yaml": "^2.8.1",
|
|
79
80
|
"zod": "^4.3.5"
|
|
80
81
|
},
|
|
@@ -92,6 +93,7 @@
|
|
|
92
93
|
"@types/retry": "^0.12.5",
|
|
93
94
|
"@types/semver": "^7.5.8",
|
|
94
95
|
"@types/uuid": "^9.0.8",
|
|
96
|
+
"@types/ws": "8.5.10",
|
|
95
97
|
"jest": "^29.7.0",
|
|
96
98
|
"memfs": "^4.17.1",
|
|
97
99
|
"nock": "13.4.0",
|
|
@@ -102,5 +104,5 @@
|
|
|
102
104
|
"typescript": "^5.5.4",
|
|
103
105
|
"uuid": "^9.0.1"
|
|
104
106
|
},
|
|
105
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "66cdccb0e7c3ec47054c3f093c25b9799d60ff36"
|
|
106
108
|
}
|