Skip to content

Commit 3525a3f

Browse files
thymikeecursoragent
andcommitted
ios: use deadline-based retry for simulator app launch
The fixed-attempt retry (5 attempts, ~12s window) is too short for CI simulators where SpringBoard needs 30-60s after boot to accept app launches — especially when the build step is cached and provides zero warm-up time. Switch to a deadline-based approach matching ensureBootedSimulator: - Default 30s timeout (configurable via AGENT_DEVICE_IOS_APP_LAUNCH_TIMEOUT_MS) - CI sets 60s to handle cold-boot scenarios - maxAttempts set high (30) so the deadline is the real limit Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 894942a commit 3525a3f

2 files changed

Lines changed: 15 additions & 2 deletions

File tree

.github/workflows/ios.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ jobs:
2626
AGENT_DEVICE_IOS_SIMCTL_LIST_TIMEOUT_MS: "60000"
2727
AGENT_DEVICE_DAEMON_TIMEOUT_MS: "300000"
2828
AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS: "180000"
29+
AGENT_DEVICE_IOS_APP_LAUNCH_TIMEOUT_MS: "60000"
2930
steps:
3031
- name: Checkout
3132
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

src/platforms/ios/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ const IOS_SIMCTL_LIST_TIMEOUT_MS = resolveTimeoutMs(
1919
TIMEOUT_PROFILES.ios_boot.operationMs,
2020
1_000,
2121
);
22+
const IOS_APP_LAUNCH_TIMEOUT_MS = resolveTimeoutMs(
23+
process.env.AGENT_DEVICE_IOS_APP_LAUNCH_TIMEOUT_MS,
24+
30_000,
25+
5_000,
26+
);
2227
const RETRY_LOGS_ENABLED = isEnvTruthy(process.env.AGENT_DEVICE_RETRY_LOGS);
2328

2429
export async function resolveIosApp(device: DeviceInfo, app: string): Promise<string> {
@@ -45,8 +50,14 @@ export async function openIosApp(device: DeviceInfo, app: string): Promise<void>
4550
if (device.kind === 'simulator') {
4651
await ensureBootedSimulator(device);
4752
await runCmd('open', ['-a', 'Simulator'], { allowFailure: true });
53+
const launchDeadline = Deadline.fromTimeoutMs(IOS_APP_LAUNCH_TIMEOUT_MS);
4854
await retryWithPolicy(
49-
async () => {
55+
async ({ deadline: attemptDeadline }) => {
56+
if (attemptDeadline?.isExpired()) {
57+
throw new AppError('COMMAND_FAILED', 'App launch deadline exceeded', {
58+
timeoutMs: IOS_APP_LAUNCH_TIMEOUT_MS,
59+
});
60+
}
5061
const result = await runCmd('xcrun', ['simctl', 'launch', device.id, bundleId], {
5162
allowFailure: true,
5263
});
@@ -60,12 +71,13 @@ export async function openIosApp(device: DeviceInfo, app: string): Promise<void>
6071
});
6172
},
6273
{
63-
maxAttempts: 5,
74+
maxAttempts: 30,
6475
baseDelayMs: 1_000,
6576
maxDelayMs: 5_000,
6677
jitter: 0.2,
6778
shouldRetry: isTransientSimulatorLaunchFailure,
6879
},
80+
{ deadline: launchDeadline },
6981
);
7082
return;
7183
}

0 commit comments

Comments
 (0)