Compare commits

...
3 Commits
Author SHA1 Message Date
Expo CI 3263ecfa36 v27.4.1 2026-11-04 00:23:17 +00:00
Gabe Debes 1da5a21f42 [simulator-launcher] Wait for startup before claiming a session (#4810)
* [simulator-launcher] Wait for startup before claiming a session

* [simulator-launcher] Report startup wait progress in job logs

* [build-tools] Log warm Simulator claim waits directly
2026-11-03 16:55:02 -07:00
Expo CI 64b9b14d09 bump STAGING and PRODUCTION to 27.4.0 in cli-versions.json 2026-11-03 17:44:51 +00:00
39 changed files with 618 additions and 229 deletions

No files matched your search

+6
View File
@@ -2,6 +2,12 @@
This is the log of notable changes to EAS CLI and related packages.
## [27.4.1](https://github.com/expo/eas-cli/releases/tag/v27.4.1) - 2026-11-04
### 🐛 Bug fixes
- [simulator-launcher][build-tools] Wait for a starting warm Simulator launcher before claiming its session, instead of falling back to a cold start. ([#4810](https://github.com/expo/eas-cli/pull/4810) by [@gwdp](https://github.com/gwdp))
## [27.4.0](https://github.com/expo/eas-cli/releases/tag/v27.4.0) - 2026-11-03
### 🎉 New features
+2 -2
View File
@@ -1,4 +1,4 @@
{
"STAGING": "27.3.0",
"PRODUCTION": "27.3.0"
"STAGING": "27.4.0",
"PRODUCTION": "27.4.0"
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"version": "27.4.0",
"version": "27.4.1",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+6
View File
@@ -1,5 +1,11 @@
# @expo/build-tools
## 27.4.1
### Patch Changes
- Wait for a starting warm Simulator launcher before claiming its session, instead of falling back to a cold start. ([#4810](https://github.com/expo/eas-cli/pull/4810) by [@gwdp](https://github.com/gwdp))
## 27.4.0
### Minor Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/build-tools",
"version": "27.4.0",
"version": "27.4.1",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
"author": "Expo <[email protected]>",
@@ -42,17 +42,17 @@
"dependencies": {
"@expo/config": "55.0.10",
"@expo/config-plugins": "55.0.7",
"@expo/downloader": "27.4.0",
"@expo/eas-build-job": "27.4.0",
"@expo/downloader": "27.4.1",
"@expo/eas-build-job": "27.4.1",
"@expo/env": "^0.4.0",
"@expo/logger": "27.4.0",
"@expo/logger": "27.4.1",
"@expo/package-manager": "1.9.10",
"@expo/plist": "^0.3.5",
"@expo/results": "^1.0.0",
"@expo/spawn-async": "1.7.2",
"@expo/steps": "27.4.0",
"@expo/template-file": "27.4.0",
"@expo/turtle-spawn": "27.4.0",
"@expo/steps": "27.4.1",
"@expo/template-file": "27.4.1",
"@expo/turtle-spawn": "27.4.1",
"@expo/xcpretty": "^4.4.7",
"@ngrok/ngrok": "1.7.0",
"@sentry/node": "7.77.0",
@@ -37,7 +37,11 @@ it('does not claim when the job does not use the warm Simulator', async () => {
expect(stopWarmSimulatorAsync).not.toHaveBeenCalled();
});
it('claims the requested device and returns the launcher record', async () => {
it('logs the wait before claiming, without logging the private launcher record', async () => {
jest.mocked(runSimulatorLauncherAsync).mockImplementationOnce(async () => {
expect(logger.info).toHaveBeenCalledWith('Waiting for the warm Simulator launcher.');
return JSON.stringify(claim);
});
const session = await tryClaimWarmIosSessionAsync({
engine: 'serve-sim',
requestedDevice: 'iPhone 17',
@@ -48,6 +52,10 @@ it('claims the requested device and returns the launcher record', async () => {
env,
signal: undefined,
});
expect(jest.mocked(logger.info).mock.calls).toEqual([
['Waiting for the warm Simulator launcher.'],
['Claimed the warm Simulator iPhone 17 (UDID).'],
]);
expect(session).toMatchObject({
device: claim.device,
server: { pid: 10, port: 3200, token: 'serve-token' },
@@ -66,6 +66,7 @@ export async function tryClaimWarmIosSessionAsync({
if (await readLocalEgressHandoffAsync()) {
throw new Error('A local egress session needs a cold start.');
}
logger.info('Waiting for the warm Simulator launcher.');
const claimArgs = ['claim', engine, ...(requestedDevice ? [requestedDevice] : [])];
const claim = ClaimSchema.parse(
JSON.parse(await runSimulatorLauncherAsync(claimArgs, { env, signal }))
@@ -1,5 +1,9 @@
# create-eas-build-function
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
@@ -1,6 +1,6 @@
{
"name": "create-eas-build-function",
"version": "27.4.0",
"version": "27.4.1",
"description": "Create functions for use in EAS Build custom builds.",
"keywords": [
"expo",
@@ -35,7 +35,7 @@
"clean": "rimraf ./build/ \"*.tsbuildinfo\""
},
"dependencies": {
"@expo/steps": "27.4.0"
"@expo/steps": "27.4.1"
},
"devDependencies": {
"@expo/package-manager": "1.9.10",
+4
View File
@@ -1,5 +1,9 @@
# @expo/downloader
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/downloader",
"version": "27.4.0",
"version": "27.4.1",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
"author": "Expo <[email protected]>",
@@ -1,5 +1,9 @@
# eas-build-cache-provider
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
@@ -1,6 +1,6 @@
{
"name": "eas-build-cache-provider",
"version": "27.4.0",
"version": "27.4.1",
"description": "A build cache provider plugin for the Expo CLI",
"homepage": "https://github.com/expo/eas-cli",
"bugs": "https://github.com/expo/eas-cli/issues",
+4
View File
@@ -1,5 +1,9 @@
# @expo/eas-build-job
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/eas-build-job",
"version": "27.4.0",
"version": "27.4.1",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "MIT",
"author": "Expo <[email protected]>",
@@ -25,9 +25,9 @@
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
},
"dependencies": {
"@expo/logger": "27.4.0",
"@expo/logger": "27.4.1",
"@expo/results": "1.0.0",
"@expo/turtle-spawn": "27.4.0",
"@expo/turtle-spawn": "27.4.1",
"joi": "^17.13.1",
"semver": "^7.6.2",
"zod": "^4.3.5"
+4
View File
@@ -1,5 +1,9 @@
# eas-cli
## 27.4.1
No changes in this release.
## 27.4.0
### Patch Changes
+146 -146
View File
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "eas-cli",
"version": "27.4.0",
"version": "27.4.1",
"description": "EAS command line tool",
"keywords": [
"cli",
@@ -48,12 +48,12 @@
"@expo/code-signing-certificates": "0.0.5",
"@expo/config": "55.0.10",
"@expo/config-plugins": "55.0.7",
"@expo/eas-build-job": "27.4.0",
"@expo/eas-json": "27.4.0",
"@expo/eas-build-job": "27.4.1",
"@expo/eas-json": "27.4.1",
"@expo/env": "^1.0.0",
"@expo/experimentation": "0.0.5",
"@expo/json-file": "8.3.3",
"@expo/logger": "27.4.0",
"@expo/logger": "27.4.1",
"@expo/multipart-body-parser": "2.0.0",
"@expo/osascript": "2.1.4",
"@expo/package-manager": "1.9.10",
@@ -65,7 +65,7 @@
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.7.2",
"@expo/steps": "27.4.0",
"@expo/steps": "27.4.1",
"@expo/timeago.js": "1.0.0",
"@oclif/core": "^4.8.3",
"@oclif/plugin-autocomplete": "^3.2.40",
+4
View File
@@ -1,5 +1,9 @@
# @expo/eas-json
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/eas-json",
"version": "27.4.0",
"version": "27.4.1",
"description": "A library for interacting with eas.json",
"homepage": "https://github.com/expo/eas-cli",
"bugs": "https://github.com/expo/eas-cli/issues",
@@ -32,7 +32,7 @@
},
"dependencies": {
"@babel/code-frame": "7.23.5",
"@expo/eas-build-job": "27.4.0",
"@expo/eas-build-job": "27.4.1",
"chalk": "4.1.2",
"env-string": "1.0.1",
"fs-extra": "11.2.0",
+4
View File
@@ -1,5 +1,9 @@
# eas-cli-local-build-plugin
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "eas-cli-local-build-plugin",
"version": "27.4.0",
"version": "27.4.1",
"description": "Tool for running EAS compatible builds on a local machine.",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
@@ -26,12 +26,12 @@
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
},
"dependencies": {
"@expo/build-tools": "27.4.0",
"@expo/build-tools": "27.4.1",
"@expo/bunyan": "^4.0.1",
"@expo/eas-build-job": "27.4.0",
"@expo/logger": "27.4.0",
"@expo/eas-build-job": "27.4.1",
"@expo/logger": "27.4.1",
"@expo/spawn-async": "^1.7.2",
"@expo/turtle-spawn": "27.4.0",
"@expo/turtle-spawn": "27.4.1",
"chalk": "4.1.2",
"env-paths": "^2.2.1",
"fs-extra": "^11.2.0",
+4
View File
@@ -1,5 +1,9 @@
# @expo/logger
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/logger",
"version": "27.4.0",
"version": "27.4.1",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
"author": "Expo <[email protected]>",
+6
View File
@@ -1,5 +1,11 @@
# @expo/simulator-launcher
## 27.4.1
### Patch Changes
- Wait for a starting warm Simulator launcher before claiming its session, instead of falling back to a cold start. ([#4810](https://github.com/expo/eas-cli/pull/4810) by [@gwdp](https://github.com/gwdp))
## 27.4.0
### Minor Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/simulator-launcher",
"version": "27.4.0",
"version": "27.4.1",
"description": "Launch and claim Simulator sessions on EAS workers",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
@@ -4,13 +4,14 @@ import path from 'node:path';
import { claimAsync } from '../commands/claim';
import { stopAsync } from '../commands/stop';
import * as launcherState from '../utils/state';
import {
LAUNCHER_STATE_FILE,
readLauncherStateAsync,
updateLauncherStateAsync,
waitForLauncherStateAsync,
} from '../utils/state';
import { deferred } from './helpers';
import { deferred, until } from './helpers';
const boot = 'boot-one';
let root: string;
@@ -30,6 +31,7 @@ const info = {
argentEventLogPath: '/argent/events',
};
const directory = () => path.join(root, 'control');
const pollLauncherStateAsync = waitForLauncherStateAsync;
const nativeFs = {
readFile: fs.readFile.bind(fs),
@@ -46,6 +48,7 @@ const remap = (file: unknown): unknown =>
: file;
beforeEach(async () => {
jest.spyOn(process.stderr, 'write').mockReturnValue(true);
root = await fs.mkdtemp(path.join(os.tmpdir(), 'launcher-state-'));
jest.spyOn(os, 'homedir').mockReturnValue(root);
await fs.mkdir(path.join(root, 'control'));
@@ -192,6 +195,7 @@ test('two job claims acquire only one ready session and the result includes its
await updateLauncherStateAsync(boot, state => ({ ...state!, status: 'claimed' }));
await Promise.all(claims);
expect(results.filter(value => value === 0)).toHaveLength(1);
expect(process.stderr.write).not.toHaveBeenCalled();
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
...info,
status: 'claimed',
@@ -200,6 +204,252 @@ test('two job claims acquire only one ready session and the result includes its
await expect(claimAsync(boot, 'serve-sim')).rejects.toThrow('available');
});
async function beginStartingClaimAsync(engine = 'serve-sim') {
let observedStarting = false;
jest
.spyOn(launcherState, 'waitForLauncherStateAsync')
.mockImplementationOnce((bootUuid, isComplete, timeoutMs) =>
pollLauncherStateAsync(
bootUuid,
state => {
const complete = isComplete(state);
observedStarting ||= state?.status === 'starting';
return complete;
},
timeoutMs
)
);
const result = claimAsync(boot, engine, 'iPhone 17').then(
value => value,
error => error as Error
);
await until(() => observedStarting);
return { result };
}
test('a claim waits for the minimal starting record to become ready without changing it', async () => {
const starting = {
bootUuid: boot,
status: 'starting' as const,
controllerPid: info.controllerPid,
launcherVersion: '27.4.0',
};
await updateLauncherStateAsync(boot, () => starting);
const output = jest.spyOn(process.stdout, 'write').mockReturnValue(true);
const { result } = await beginStartingClaimAsync();
expect(await readLauncherStateAsync(boot)).toEqual(starting);
expect(await fs.readdir(directory())).toEqual([path.basename(LAUNCHER_STATE_FILE)]);
expect(output).not.toHaveBeenCalled();
await updateLauncherStateAsync(boot, state => ({ ...state!, ...info, status: 'ready' }));
await waitForLauncherStateAsync(boot, state => state?.status === 'claiming', 1_000);
await updateLauncherStateAsync(boot, state => ({ ...state!, status: 'claimed' }));
expect(await result).toBe(0);
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
...info,
status: 'claimed',
engine: 'serve-sim',
requestedDevice: 'iPhone 17',
});
});
test('two claims waiting for startup still acquire only one session', async () => {
await updateLauncherStateAsync(boot, () => ({
bootUuid: boot,
status: 'starting',
controllerPid: info.controllerPid,
}));
jest.spyOn(process.stdout, 'write').mockReturnValue(true);
const first = await beginStartingClaimAsync();
const second = await beginStartingClaimAsync('argent');
await updateLauncherStateAsync(boot, state => ({ ...state!, ...info, status: 'ready' }));
await waitForLauncherStateAsync(boot, state => state?.status === 'claiming', 1_000);
await updateLauncherStateAsync(boot, state => ({ ...state!, status: 'claimed' }));
const results = await Promise.all([first.result, second.result]);
expect(results.filter(value => value === 0)).toHaveLength(1);
expect(results.filter(value => value instanceof Error)).toHaveLength(1);
});
test('startup wait progress is throttled and stdout remains the claim JSON', async () => {
let now = 0;
jest.spyOn(Date, 'now').mockImplementation(() => now);
const output = jest.spyOn(process.stdout, 'write').mockReturnValue(true);
const progress = jest.mocked(process.stderr.write);
await updateLauncherStateAsync(boot, () => ({
bootUuid: boot,
status: 'starting',
controllerPid: info.controllerPid,
}));
const { result } = await beginStartingClaimAsync();
expect(progress.mock.calls).toEqual([
['Warm Simulator is still starting; waiting (0s elapsed, 420s limit).\n'],
]);
const reads = jest.mocked(fs.readFile).mock.calls.length;
now = 10_000;
await until(() => jest.mocked(fs.readFile).mock.calls.length >= reads + 2);
expect(progress).toHaveBeenCalledTimes(1);
expect(output).not.toHaveBeenCalled();
now = 30_000;
await until(() => progress.mock.calls.length === 2);
expect(progress.mock.calls[1]).toEqual([
'Warm Simulator is still starting; waiting (30s elapsed, 420s limit).\n',
]);
now = 31_000;
await updateLauncherStateAsync(boot, state => ({ ...state!, ...info, status: 'ready' }));
await waitForLauncherStateAsync(boot, state => state?.status === 'claiming', 1_000);
await updateLauncherStateAsync(boot, state => ({ ...state!, status: 'claimed' }));
expect(await result).toBe(0);
expect(progress.mock.calls).toHaveLength(3);
expect(progress.mock.calls[2]).toEqual(['Warm Simulator became ready after waiting 31s.\n']);
expect(output).toHaveBeenCalledTimes(1);
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
status: 'claimed',
server: { token: info.server.token },
});
expect(progress.mock.calls.flat().join('')).not.toContain(info.server.token);
});
test.each(['stopping', 'disabled'] as const)(
'a waiting claim preserves a startup failure in %s state',
async status => {
await updateLauncherStateAsync(boot, () => ({
bootUuid: boot,
status: 'starting',
controllerPid: info.controllerPid,
}));
const { result } = await beginStartingClaimAsync();
await updateLauncherStateAsync(boot, state => ({
...state!,
status,
error: 'serve-sim exited',
}));
expect(await result).toEqual(
new Error(
'Simulator preparation was interrupted; cleanup may still be running. serve-sim exited'
)
);
expect((await readLauncherStateAsync(boot))?.status).toBe(status);
}
);
test.each(['missing', 'old boot', 'different controller', 'different start time'] as const)(
'a waiting claim cannot acquire %s state',
async replacement => {
await updateLauncherStateAsync(boot, () => ({
bootUuid: boot,
status: 'starting',
controllerPid: info.controllerPid,
controllerStartTime: info.controllerStartTime,
}));
const { result } = await beginStartingClaimAsync();
if (replacement === 'missing') {
await fs.rm(LAUNCHER_STATE_FILE);
} else if (replacement === 'old boot') {
await updateLauncherStateAsync('other-boot', () => ({
bootUuid: 'other-boot',
status: 'ready',
...info,
}));
} else {
await updateLauncherStateAsync(boot, state => ({
...state!,
...info,
status: 'ready',
...(replacement === 'different controller'
? { controllerPid: info.controllerPid + 1 }
: { controllerStartTime: 'replacement-start' }),
}));
}
expect(await result).toBeInstanceOf(Error);
expect((await readLauncherStateAsync(boot))?.status).not.toBe('claiming');
}
);
test('a claim rechecks controller identity under the state lock', async () => {
await updateLauncherStateAsync(boot, () => ({ bootUuid: boot, status: 'ready', ...info }));
jest.spyOn(fs, 'mkdir').mockImplementationOnce(async file => {
await nativeFs.writeFile(
remap(LAUNCHER_STATE_FILE) as string,
JSON.stringify({ bootUuid: boot, status: 'ready', ...info, controllerPid: 9002 })
);
await nativeFs.mkdir(remap(file) as string);
return undefined;
});
await expect(claimAsync(boot, 'serve-sim')).rejects.toThrow('different session');
expect(await readLauncherStateAsync(boot)).toMatchObject({
status: 'ready',
controllerPid: 9002,
});
});
test('startup and claim acknowledgement share one timeout', async () => {
let now = 0;
jest.spyOn(Date, 'now').mockImplementation(() => now);
await updateLauncherStateAsync(boot, () => ({
bootUuid: boot,
status: 'starting',
controllerPid: info.controllerPid,
}));
const { result } = await beginStartingClaimAsync();
now = 419_000;
await updateLauncherStateAsync(boot, state => ({ ...state!, ...info, status: 'ready' }));
await waitForLauncherStateAsync(boot, state => state?.status === 'claiming', 1_000);
now = 420_001;
let settled = false;
void result.then(() => {
settled = true;
});
try {
await until(() => settled);
expect(await result).toEqual(
new Error('Timed out waiting for the Simulator launcher; cleanup may still be running.')
);
} finally {
now = 1_000_000;
await result;
}
});
test('startup times out without acquiring or changing the starting record', async () => {
let now = 0;
jest.spyOn(Date, 'now').mockImplementation(() => now);
await updateLauncherStateAsync(boot, () => ({
bootUuid: boot,
status: 'starting',
controllerPid: info.controllerPid,
}));
const { result } = await beginStartingClaimAsync();
now = 420_001;
expect(await result).toEqual(
new Error('Timed out waiting for the Simulator launcher; cleanup may still be running.')
);
expect((await readLauncherStateAsync(boot))?.status).toBe('starting');
});
test('a claim cannot acquire after the deadline expires while taking the state lock', async () => {
let now = 0;
jest.spyOn(Date, 'now').mockImplementation(() => now);
await updateLauncherStateAsync(boot, () => ({ bootUuid: boot, status: 'ready', ...info }));
jest.spyOn(fs, 'mkdir').mockImplementationOnce(async file => {
now = 420_001;
await nativeFs.mkdir(remap(file) as string);
return undefined;
});
await expect(claimAsync(boot, 'serve-sim')).rejects.toThrow('Timed out');
expect((await readLauncherStateAsync(boot))?.status).toBe('ready');
});
test.each(['missing', 'old boot', 'starting without a controller'] as const)(
'a claim immediately rejects %s state',
async state => {
if (state !== 'missing') {
const bootUuid = state === 'old boot' ? 'old-boot' : boot;
await updateLauncherStateAsync(bootUuid, () => ({ bootUuid, status: 'starting' }));
}
await expect(claimAsync(boot, 'serve-sim')).rejects.toThrow('available');
}
);
test('a claim fails when the launcher hands over a different device than requested', async () => {
await updateLauncherStateAsync(boot, () => ({ bootUuid: boot, status: 'ready', ...info }));
const claim = expect(claimAsync(boot, 'serve-sim', 'iPad Pro')).rejects.toThrow(
@@ -1,5 +1,6 @@
import {
ClaimInputSchema,
type LauncherState,
WarmSessionInfoSchema,
updateLauncherStateAsync,
waitForLauncherStateAsync,
@@ -16,13 +17,72 @@ export async function claimAsync(
`Invalid Simulator session request. Choose ${ClaimInputSchema.shape.engine.options.join(', ')} and an optional device name or UDID.`
);
}
// Startup and handoff share the worker's existing claim timeout.
const startedAt = Date.now();
const deadline = startedAt + 420_000;
let lastWaitLogAt: number | undefined;
let controller: LauncherState | undefined;
const ready = await waitForLauncherStateAsync(
bootUuid,
state => {
if (state?.status === 'stopping' || state?.status === 'disabled' || state?.error) {
throw new Error(
`Simulator preparation was interrupted; cleanup may still be running.${state.error ? ` ${state.error}` : ''}`
);
}
if (
!state ||
(state.status !== 'starting' && state.status !== 'ready') ||
state.controllerPid === undefined
) {
throw new Error('No warm Simulator session is available to claim on this VM.');
}
if (
controller &&
(state.controllerPid !== controller.controllerPid ||
(controller.controllerStartTime !== undefined &&
state.controllerStartTime !== controller.controllerStartTime))
) {
throw new Error('The Simulator launcher returned a different session or engine.');
}
controller = state;
if (state.status === 'starting') {
const now = Date.now();
if (lastWaitLogAt === undefined || now - lastWaitLogAt >= 30_000) {
process.stderr.write(
`Warm Simulator is still starting; waiting (${Math.floor((now - startedAt) / 1_000)}s elapsed, 420s limit).\n`
);
lastWaitLogAt = now;
}
}
return state.status === 'ready';
},
deadline - Date.now()
);
const prepared = WarmSessionInfoSchema.parse(ready);
const acquired = await updateLauncherStateAsync(bootUuid, state => {
if (state?.status !== 'ready') {
throw new Error('No warm Simulator session is available to claim on this VM.');
}
if (
state.controllerPid !== prepared.controllerPid ||
state.controllerStartTime !== prepared.controllerStartTime
) {
throw new Error('The Simulator launcher returned a different session or engine.');
}
if (Date.now() >= deadline) {
throw new Error(
'Timed out waiting for the Simulator launcher; cleanup may still be running.'
);
}
WarmSessionInfoSchema.parse(state);
return { ...state, ...input.data, status: 'claiming' };
});
if (lastWaitLogAt !== undefined) {
process.stderr.write(
`Warm Simulator became ready after waiting ${Math.floor((Date.now() - startedAt) / 1_000)}s.\n`
);
}
const claimed = await waitForLauncherStateAsync(
bootUuid,
state => {
@@ -40,7 +100,7 @@ export async function claimAsync(
}
return state.status === 'claimed';
},
420_000
Math.max(0, deadline - Date.now())
);
const { device } = WarmSessionInfoSchema.parse(claimed);
if (
+4
View File
@@ -1,5 +1,9 @@
# @expo/steps
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/steps",
"version": "27.4.0",
"version": "27.4.1",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
"author": "Expo <[email protected]>",
@@ -27,8 +27,8 @@
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
},
"dependencies": {
"@expo/eas-build-job": "27.4.0",
"@expo/logger": "27.4.0",
"@expo/eas-build-job": "27.4.1",
"@expo/logger": "27.4.1",
"@expo/spawn-async": "^1.7.2",
"arg": "^5.0.2",
"fast-glob": "^3.3.2",
+4
View File
@@ -1,5 +1,9 @@
# @expo/template-file
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/template-file",
"version": "27.4.0",
"version": "27.4.1",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
"author": "Expo <[email protected]>",
+4
View File
@@ -1,5 +1,9 @@
# @expo/turtle-spawn
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/turtle-spawn",
"version": "27.4.0",
"version": "27.4.1",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
"author": "Expo <[email protected]>",
@@ -25,7 +25,7 @@
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
},
"dependencies": {
"@expo/logger": "27.4.0",
"@expo/logger": "27.4.1",
"@expo/spawn-async": "^1.7.2"
},
"devDependencies": {
+4
View File
@@ -1,5 +1,9 @@
# @expo/vm-tools
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/vm-tools",
"version": "27.4.0",
"version": "27.4.1",
"description": "VM startup script loader",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
+4
View File
@@ -1,5 +1,9 @@
# @expo/worker
## 27.4.1
No changes in this release.
## 27.4.0
No changes in this release.
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/worker",
"version": "27.4.0",
"version": "27.4.1",
"private": true,
"scripts": {
"start": "yarn watch",
@@ -19,17 +19,17 @@
},
"dependencies": {
"@babel/core": "7.24.7",
"@expo/build-tools": "27.4.0",
"@expo/build-tools": "27.4.1",
"@expo/config-plugins": "55.0.7",
"@expo/config-types": "55.0.5",
"@expo/eas-build-job": "27.4.0",
"@expo/logger": "27.4.0",
"@expo/eas-build-job": "27.4.1",
"@expo/logger": "27.4.1",
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.7.2",
"@expo/steps": "27.4.0",
"@expo/template-file": "27.4.0",
"@expo/turtle-spawn": "27.4.0",
"@expo/steps": "27.4.1",
"@expo/template-file": "27.4.1",
"@expo/turtle-spawn": "27.4.1",
"@hapi/boom": "10.0.1",
"check-disk-space": "3.4.0",
"fast-glob": "3.3.2",
Generated
+35 -35
View File
@@ -2515,24 +2515,24 @@ __metadata:
languageName: node
linkType: hard
"@expo/build-tools@npm:27.4.0, @expo/build-tools@workspace:packages/build-tools":
"@expo/build-tools@npm:27.4.1, @expo/build-tools@workspace:packages/build-tools":
version: 0.0.0-use.local
resolution: "@expo/build-tools@workspace:packages/build-tools"
dependencies:
"@expo/config": "npm:55.0.10"
"@expo/config-plugins": "npm:55.0.7"
"@expo/downloader": "npm:27.4.0"
"@expo/eas-build-job": "npm:27.4.0"
"@expo/downloader": "npm:27.4.1"
"@expo/eas-build-job": "npm:27.4.1"
"@expo/env": "npm:^0.4.0"
"@expo/logger": "npm:27.4.0"
"@expo/logger": "npm:27.4.1"
"@expo/package-manager": "npm:1.9.10"
"@expo/plist": "npm:^0.3.5"
"@expo/repack-app": "npm:~0.9.0"
"@expo/results": "npm:^1.0.0"
"@expo/spawn-async": "npm:1.7.2"
"@expo/steps": "npm:27.4.0"
"@expo/template-file": "npm:27.4.0"
"@expo/turtle-spawn": "npm:27.4.0"
"@expo/steps": "npm:27.4.1"
"@expo/template-file": "npm:27.4.1"
"@expo/turtle-spawn": "npm:27.4.1"
"@expo/xcpretty": "npm:^4.4.7"
"@ngrok/ngrok": "npm:1.7.0"
"@sentry/node": "npm:7.77.0"
@@ -2768,7 +2768,7 @@ __metadata:
languageName: node
linkType: hard
"@expo/downloader@npm:27.4.0, @expo/downloader@workspace:packages/downloader":
"@expo/downloader@npm:27.4.1, @expo/downloader@workspace:packages/downloader":
version: 0.0.0-use.local
resolution: "@expo/downloader@workspace:packages/downloader"
dependencies:
@@ -2784,13 +2784,13 @@ __metadata:
languageName: unknown
linkType: soft
"@expo/eas-build-job@npm:27.4.0, @expo/eas-build-job@workspace:packages/eas-build-job":
"@expo/eas-build-job@npm:27.4.1, @expo/eas-build-job@workspace:packages/eas-build-job":
version: 0.0.0-use.local
resolution: "@expo/eas-build-job@workspace:packages/eas-build-job"
dependencies:
"@expo/logger": "npm:27.4.0"
"@expo/logger": "npm:27.4.1"
"@expo/results": "npm:1.0.0"
"@expo/turtle-spawn": "npm:27.4.0"
"@expo/turtle-spawn": "npm:27.4.1"
"@types/jest": "npm:^29.5.12"
"@types/node": "npm:22.20.5"
jest: "npm:^29.7.0"
@@ -2803,12 +2803,12 @@ __metadata:
languageName: unknown
linkType: soft
"@expo/eas-json@npm:27.4.0, @expo/eas-json@workspace:packages/eas-json":
"@expo/eas-json@npm:27.4.1, @expo/eas-json@workspace:packages/eas-json":
version: 0.0.0-use.local
resolution: "@expo/eas-json@workspace:packages/eas-json"
dependencies:
"@babel/code-frame": "npm:7.23.5"
"@expo/eas-build-job": "npm:27.4.0"
"@expo/eas-build-job": "npm:27.4.1"
"@tsconfig/node22": "npm:22.0.6"
"@types/babel__code-frame": "npm:7.0.3"
"@types/fs-extra": "npm:11.0.4"
@@ -2931,7 +2931,7 @@ __metadata:
languageName: node
linkType: hard
"@expo/logger@npm:27.4.0, @expo/logger@workspace:packages/logger":
"@expo/logger@npm:27.4.1, @expo/logger@workspace:packages/logger":
version: 0.0.0-use.local
resolution: "@expo/logger@workspace:packages/logger"
dependencies:
@@ -3147,12 +3147,12 @@ __metadata:
languageName: node
linkType: hard
"@expo/steps@npm:27.4.0, @expo/steps@workspace:packages/steps":
"@expo/steps@npm:27.4.1, @expo/steps@workspace:packages/steps":
version: 0.0.0-use.local
resolution: "@expo/steps@workspace:packages/steps"
dependencies:
"@expo/eas-build-job": "npm:27.4.0"
"@expo/logger": "npm:27.4.0"
"@expo/eas-build-job": "npm:27.4.1"
"@expo/logger": "npm:27.4.1"
"@expo/spawn-async": "npm:^1.7.2"
"@jest/globals": "npm:^29.7.0"
"@types/jest": "npm:^29.5.12"
@@ -3178,7 +3178,7 @@ __metadata:
languageName: unknown
linkType: soft
"@expo/template-file@npm:27.4.0, @expo/template-file@workspace:packages/template-file":
"@expo/template-file@npm:27.4.1, @expo/template-file@workspace:packages/template-file":
version: 0.0.0-use.local
resolution: "@expo/template-file@workspace:packages/template-file"
dependencies:
@@ -3201,11 +3201,11 @@ __metadata:
languageName: node
linkType: hard
"@expo/turtle-spawn@npm:27.4.0, @expo/turtle-spawn@workspace:packages/turtle-spawn":
"@expo/turtle-spawn@npm:27.4.1, @expo/turtle-spawn@workspace:packages/turtle-spawn":
version: 0.0.0-use.local
resolution: "@expo/turtle-spawn@workspace:packages/turtle-spawn"
dependencies:
"@expo/logger": "npm:27.4.0"
"@expo/logger": "npm:27.4.1"
"@expo/spawn-async": "npm:^1.7.2"
"@types/jest": "npm:^29.5.12"
"@types/node": "npm:22.20.5"
@@ -3236,17 +3236,17 @@ __metadata:
resolution: "@expo/worker@workspace:packages/worker"
dependencies:
"@babel/core": "npm:7.24.7"
"@expo/build-tools": "npm:27.4.0"
"@expo/build-tools": "npm:27.4.1"
"@expo/config-plugins": "npm:55.0.7"
"@expo/config-types": "npm:55.0.5"
"@expo/eas-build-job": "npm:27.4.0"
"@expo/logger": "npm:27.4.0"
"@expo/eas-build-job": "npm:27.4.1"
"@expo/logger": "npm:27.4.1"
"@expo/results": "npm:1.0.0"
"@expo/rudder-sdk-node": "npm:1.1.1"
"@expo/spawn-async": "npm:1.7.2"
"@expo/steps": "npm:27.4.0"
"@expo/template-file": "npm:27.4.0"
"@expo/turtle-spawn": "npm:27.4.0"
"@expo/steps": "npm:27.4.1"
"@expo/template-file": "npm:27.4.1"
"@expo/turtle-spawn": "npm:27.4.1"
"@hapi/boom": "npm:10.0.1"
"@types/babel__core": "npm:7.20.5"
"@types/fs-extra": "npm:11.0.4"
@@ -10130,7 +10130,7 @@ __metadata:
resolution: "create-eas-build-function@workspace:packages/create-eas-build-function"
dependencies:
"@expo/package-manager": "npm:1.9.10"
"@expo/steps": "npm:27.4.0"
"@expo/steps": "npm:27.4.1"
"@jest/globals": "npm:^29.7.0"
"@types/jest": "npm:^29.5.12"
"@types/node": "npm:22.20.5"
@@ -10714,12 +10714,12 @@ __metadata:
version: 0.0.0-use.local
resolution: "eas-cli-local-build-plugin@workspace:packages/local-build-plugin"
dependencies:
"@expo/build-tools": "npm:27.4.0"
"@expo/build-tools": "npm:27.4.1"
"@expo/bunyan": "npm:^4.0.1"
"@expo/eas-build-job": "npm:27.4.0"
"@expo/logger": "npm:27.4.0"
"@expo/eas-build-job": "npm:27.4.1"
"@expo/logger": "npm:27.4.1"
"@expo/spawn-async": "npm:^1.7.2"
"@expo/turtle-spawn": "npm:27.4.0"
"@expo/turtle-spawn": "npm:27.4.1"
"@types/fs-extra": "npm:^11.0.4"
"@types/hapi__joi": "npm:^17.1.14"
"@types/lodash": "npm:^4.17.4"
@@ -10773,12 +10773,12 @@ __metadata:
"@expo/code-signing-certificates": "npm:0.0.5"
"@expo/config": "npm:55.0.10"
"@expo/config-plugins": "npm:55.0.7"
"@expo/eas-build-job": "npm:27.4.0"
"@expo/eas-json": "npm:27.4.0"
"@expo/eas-build-job": "npm:27.4.1"
"@expo/eas-json": "npm:27.4.1"
"@expo/env": "npm:^1.0.0"
"@expo/experimentation": "npm:0.0.5"
"@expo/json-file": "npm:8.3.3"
"@expo/logger": "npm:27.4.0"
"@expo/logger": "npm:27.4.1"
"@expo/multipart-body-parser": "npm:2.0.0"
"@expo/osascript": "npm:2.1.4"
"@expo/package-manager": "npm:1.9.10"
@@ -10790,7 +10790,7 @@ __metadata:
"@expo/results": "npm:1.0.0"
"@expo/rudder-sdk-node": "npm:1.1.1"
"@expo/spawn-async": "npm:1.7.2"
"@expo/steps": "npm:27.4.0"
"@expo/steps": "npm:27.4.1"
"@expo/timeago.js": "npm:1.0.0"
"@graphql-codegen/cli": "npm:6.3.1"
"@graphql-codegen/introspection": "npm:5.0.2"