boot: add --install-ipa auto-install option (#236)

This commit is contained in:
James Jackson
2026-03-17 22:49:18 -04:00
committed by GitHub
parent c7205642e5
commit 1e3d6d75ee
2 changed files with 53 additions and 0 deletions

View File

@@ -13,6 +13,7 @@ class VPhoneAppDelegate: NSObject, NSApplicationDelegate {
private var appWindowController: VPhoneAppWindowController?
private var locationProvider: VPhoneLocationProvider?
private var sigintSource: DispatchSourceSignal?
private var didAttemptAutoInstall = false
init(cli: VPhoneBootCLI) {
self.cli = cli
@@ -154,6 +155,9 @@ class VPhoneAppDelegate: NSObject, NSApplicationDelegate {
}
mc?.syncBatteryFromHost()
mc?.syncLowPowerModeFromHost()
Task { @MainActor [weak self] in
await self?.installPackageIfRequested(caps: caps)
}
}
control.onDisconnect = { [weak mc, weak provider = locationProvider] in
mc?.updateConnectAvailability(available: false)
@@ -174,6 +178,9 @@ class VPhoneAppDelegate: NSObject, NSApplicationDelegate {
} else {
print("[location] guest does not support location simulation")
}
Task { @MainActor [weak self] in
await self?.installPackageIfRequested(caps: caps)
}
}
control.onDisconnect = { [weak provider = locationProvider] in
provider?.stopReplay()
@@ -182,6 +189,42 @@ class VPhoneAppDelegate: NSObject, NSApplicationDelegate {
}
}
@MainActor
private func installPackageIfRequested(caps: [String]) async {
guard !didAttemptAutoInstall else { return }
guard let packageURL = cli.installPackageURL else { return }
guard FileManager.default.fileExists(atPath: packageURL.path) else {
didAttemptAutoInstall = true
print("[install] requested package not found: \(packageURL.path)")
return
}
guard VPhoneInstallPackage.isSupportedFile(packageURL) else {
didAttemptAutoInstall = true
print("[install] unsupported package type: \(packageURL.path)")
return
}
guard caps.contains("ipa_install") else {
print(
"[install] guest does not advertise ipa_install; reconnect or reboot the guest so the updated daemon can take over"
)
return
}
guard let control else {
print("[install] control channel is not ready")
return
}
didAttemptAutoInstall = true
print("[install] auto-installing \(packageURL.lastPathComponent)")
do {
let result = try await control.installIPA(localURL: packageURL)
print("[install] \(result)")
} catch {
print("[install] failed: \(error)")
}
}
func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool {
!cli.noGraphics
}

View File

@@ -44,11 +44,21 @@ struct VPhoneBootCLI: ParsableCommand {
@Option(help: "Path to signed vphoned binary for guest auto-update")
var vphonedBin: String = ".vphoned.signed"
@Option(
help: "Automatically install the given IPA/TIPA after the guest control channel connects.",
transform: URL.init(fileURLWithPath:)
)
var installIPA: URL?
/// DFU mode runs headless (no GUI).
var noGraphics: Bool {
dfu
}
var installPackageURL: URL? {
installIPA?.standardizedFileURL
}
/// Resolve final options by merging manifest values.
func resolveOptions() throws -> VPhoneVirtualMachine.Options {
let manifest = try VPhoneVirtualMachineManifest.load(from: config)