| 36 | * Launch an app on a physical device and return the process ID if available. |
| 37 | */ |
| 38 | export async function launchAppOnDevice( |
| 39 | deviceId: string, |
| 40 | bundleId: string, |
| 41 | executor: CommandExecutor, |
| 42 | fileSystem: FileSystemExecutor, |
| 43 | opts?: { env?: Record<string, string>; args?: string[] }, |
| 44 | ): Promise<LaunchStepResult> { |
| 45 | log('info', `Launching app ${bundleId} on device ${deviceId}`); |
| 46 | const tempJsonPath = join(fileSystem.tmpdir(), `launch-${Date.now()}.json`); |
| 47 | |
| 48 | const command = [ |
| 49 | 'xcrun', |
| 50 | 'devicectl', |
| 51 | 'device', |
| 52 | 'process', |
| 53 | 'launch', |
| 54 | '--device', |
| 55 | deviceId, |
| 56 | '--json-output', |
| 57 | tempJsonPath, |
| 58 | '--terminate-existing', |
| 59 | ]; |
| 60 | |
| 61 | if (opts?.env && Object.keys(opts.env).length > 0) { |
| 62 | command.push('--environment-variables', JSON.stringify(opts.env)); |
| 63 | } |
| 64 | |
| 65 | command.push(bundleId); |
| 66 | |
| 67 | if (opts?.args?.length) { |
| 68 | command.push(...opts.args); |
| 69 | } |
| 70 | |
| 71 | const result = await executor(command, 'Launch app on device', false); |
| 72 | if (!result.success) { |
| 73 | await fileSystem.rm(tempJsonPath, { force: true }).catch(() => {}); |
| 74 | return { success: false, error: result.error ?? 'Failed to launch app' }; |
| 75 | } |
| 76 | |
| 77 | let processId: number | undefined; |
| 78 | try { |
| 79 | const jsonContent = await fileSystem.readFile(tempJsonPath, 'utf8'); |
| 80 | const parsedData = JSON.parse(jsonContent) as { |
| 81 | result?: { process?: { processIdentifier?: unknown } }; |
| 82 | }; |
| 83 | const pid = parsedData?.result?.process?.processIdentifier; |
| 84 | if (typeof pid === 'number') { |
| 85 | processId = pid; |
| 86 | } |
| 87 | } catch { |
| 88 | log('warn', 'Failed to parse launch JSON output for process ID'); |
| 89 | } finally { |
| 90 | await fileSystem.rm(tempJsonPath, { force: true }).catch(() => {}); |
| 91 | } |
| 92 | |
| 93 | return { success: true, processId }; |
| 94 | } |