From c94de821238b664b59a20c68c89e8b190ae04fe4 Mon Sep 17 00:00:00 2001 From: fedor Date: Mon, 22 Jan 2024 14:57:24 -0500 Subject: [PATCH] Fixed paths and formatting --- .cirrus.yml | 4 +- Sources/tart/Commands/Create.swift | 36 ++--- Sources/tart/Commands/Run.swift | 86 ++++++------ Sources/tart/Platform/Darwin.swift | 216 ++++++++++++++--------------- Sources/tart/VM.swift | 144 +++++++++---------- Sources/tart/VMConfig.swift | 10 +- 6 files changed, 248 insertions(+), 248 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 63156e3..92813b0 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -60,9 +60,9 @@ task: macos_instance: image: ghcr.io/cirruslabs/macos-sonoma-xcode:$XCODE_TAG build_script: swift build --arch $BUILD_ARCH --product tart - sign_script: codesign --sign - --entitlements Resources/tart-dev.entitlements --force .build/$BUILD_ARCH-apple-macosx/tart + sign_script: codesign --sign - --entitlements Resources/tart-dev.entitlements --force .build/$BUILD_ARCH-apple-macosx/debug/tart binary_artifacts: - path: .build/$BUILD_ARCH-apple-macosx/tart + path: .build/$BUILD_ARCH-apple-macosx/debug/tart task: only_if: $CIRRUS_TAG == '' && ($CIRRUS_USER_PERMISSION == 'write' || $CIRRUS_USER_PERMISSION == 'admin') diff --git a/Sources/tart/Commands/Create.swift b/Sources/tart/Commands/Create.swift index cd27458..b816fc2 100644 --- a/Sources/tart/Commands/Create.swift +++ b/Sources/tart/Commands/Create.swift @@ -24,9 +24,9 @@ struct Create: AsyncParsableCommand { throw ValidationError("Please specify either a --from-ipsw or --linux option!") } #if arch(x86_64) - if fromIPSW != nil { - throw ValidationError("Only Linux VMs are supported on Intel!") - } + if fromIPSW != nil { + throw ValidationError("Only Linux VMs are supported on Intel!") + } #endif } @@ -39,27 +39,27 @@ struct Create: AsyncParsableCommand { try await withTaskCancellationHandler(operation: { #if arch(arm64) - if let fromIPSW = fromIPSW { - let ipswURL: URL + if let fromIPSW = fromIPSW { + let ipswURL: URL - if fromIPSW == "latest" { - defaultLogger.appendNewLine("Looking up the latest supported IPSW...") + if fromIPSW == "latest" { + defaultLogger.appendNewLine("Looking up the latest supported IPSW...") - let image = try await withCheckedThrowingContinuation { continuation in - VZMacOSRestoreImage.fetchLatestSupported() { result in - continuation.resume(with: result) + let image = try await withCheckedThrowingContinuation { continuation in + VZMacOSRestoreImage.fetchLatestSupported() { result in + continuation.resume(with: result) + } } + + ipswURL = image.url + } else if fromIPSW.starts(with: "http://") || fromIPSW.starts(with: "https://") { + ipswURL = URL(string: fromIPSW)! + } else { + ipswURL = URL(fileURLWithPath: NSString(string: fromIPSW).expandingTildeInPath) } - ipswURL = image.url - } else if fromIPSW.starts(with: "http://") || fromIPSW.starts(with: "https://") { - ipswURL = URL(string: fromIPSW)! - } else { - ipswURL = URL(fileURLWithPath: NSString(string: fromIPSW).expandingTildeInPath) + _ = try await VM(vmDir: tmpVMDir, ipswURL: ipswURL, diskSizeGB: diskSize) } - - _ = try await VM(vmDir: tmpVMDir, ipswURL: ipswURL, diskSizeGB: diskSize) - } #endif if linux { diff --git a/Sources/tart/Commands/Run.swift b/Sources/tart/Commands/Run.swift index 9c0ab40..4f46ab1 100644 --- a/Sources/tart/Commands/Run.swift +++ b/Sources/tart/Commands/Run.swift @@ -37,23 +37,23 @@ struct Run: AsyncParsableCommand { var graphics: Bool = false #if arch(arm64) - @Flag(help: "Boot into recovery mode") + @Flag(help: "Boot into recovery mode") #endif var recovery: Bool = false #if arch(arm64) - @Flag(help: ArgumentHelp( - "Use screen sharing instead of the built-in UI.", - discussion: "Useful since Screen Sharing supports copy/paste, drag and drop, etc.\n" - + "Note that Remote Login option should be enabled inside the VM.")) + @Flag(help: ArgumentHelp( + "Use screen sharing instead of the built-in UI.", + discussion: "Useful since Screen Sharing supports copy/paste, drag and drop, etc.\n" + + "Note that Remote Login option should be enabled inside the VM.")) #endif var vnc: Bool = false #if arch(arm64) - @Flag(help: ArgumentHelp( - "Use Virtualization.Framework's VNC server instead of the build-in UI.", - discussion: "Useful since this type of VNC is available in recovery mode and in macOS installation.\n" - + "Note that this feature is experimental and there may be bugs present when using VNC.")) + @Flag(help: ArgumentHelp( + "Use Virtualization.Framework's VNC server instead of the build-in UI.", + discussion: "Useful since this type of VNC is available in recovery mode and in macOS installation.\n" + + "Note that this feature is experimental and there may be bugs present when using VNC.")) #endif var vncExperimental: Bool = false @@ -73,18 +73,18 @@ struct Run: AsyncParsableCommand { var disk: [String] = [] #if arch(arm64) - @Option(name: [.customLong("rosetta")], help: ArgumentHelp( - "Attaches a Rosetta share to the guest Linux VM with a specific tag (e.g. --rosetta=\"rosetta\")", - discussion: """ - Requires host to be macOS 13.0 (Ventura) with Rosetta installed. The latter can be done - by running "softwareupdate --install-rosetta" (without quotes) in the Terminal.app. + @Option(name: [.customLong("rosetta")], help: ArgumentHelp( + "Attaches a Rosetta share to the guest Linux VM with a specific tag (e.g. --rosetta=\"rosetta\")", + discussion: """ + Requires host to be macOS 13.0 (Ventura) with Rosetta installed. The latter can be done + by running "softwareupdate --install-rosetta" (without quotes) in the Terminal.app. - Note that you also have to configure Rosetta in the guest Linux VM by following the - steps from "Mount the Shared Directory and Register Rosetta" section here: - https://developer.apple.com/documentation/virtualization/running_intel_binaries_in_linux_vms_with_rosetta#3978496 - """, - valueName: "tag" - )) + Note that you also have to configure Rosetta in the guest Linux VM by following the + steps from "Mount the Shared Directory and Register Rosetta" section here: + https://developer.apple.com/documentation/virtualization/running_intel_binaries_in_linux_vms_with_rosetta#3978496 + """, + valueName: "tag" + )) #endif var rosettaTag: String? @@ -114,13 +114,13 @@ struct Run: AsyncParsableCommand { var netSoftnet: Bool = false #if arch(arm64) - @Flag(help: ArgumentHelp("Disables audio and entropy devices and switches to only Mac-specific input devices.", discussion: "Useful for running a VM that can be suspended via \"tart suspend\".")) + @Flag(help: ArgumentHelp("Disables audio and entropy devices and switches to only Mac-specific input devices.", discussion: "Useful for running a VM that can be suspended via \"tart suspend\".")) #endif var suspendable: Bool = false #if arch(arm64) - @Flag(help: ArgumentHelp("Whether system hot keys should be sent to the guest instead of the host", - discussion: "If enabled then system hot keys like Cmd+Tab will be sent to the guest instead of the host.")) + @Flag(help: ArgumentHelp("Whether system hot keys should be sent to the guest instead of the host", + discussion: "If enabled then system hot keys like Cmd+Tab will be sent to the guest instead of the host.")) #endif var captureSystemKeys: Bool = false @@ -249,15 +249,15 @@ struct Run: AsyncParsableCommand { var resume = false #if arch(arm64) - if #available(macOS 14, *) { - if FileManager.default.fileExists(atPath: vmDir.stateURL.path) { - print("restoring VM state from a snapshot...") - try await vm!.virtualMachine.restoreMachineStateFrom(url: vmDir.stateURL) - try FileManager.default.removeItem(at: vmDir.stateURL) - resume = true - print("resuming VM...") + if #available(macOS 14, *) { + if FileManager.default.fileExists(atPath: vmDir.stateURL.path) { + print("restoring VM state from a snapshot...") + try await vm!.virtualMachine.restoreMachineStateFrom(url: vmDir.stateURL) + try FileManager.default.removeItem(at: vmDir.stateURL) + resume = true + print("resuming VM...") + } } - } #endif try await vm!.start(recovery: recovery, resume: resume) @@ -305,23 +305,23 @@ struct Run: AsyncParsableCommand { Task { do { #if arch(arm64) - if #available(macOS 14, *) { - try vm!.configuration.validateSaveRestoreSupport() + if #available(macOS 14, *) { + try vm!.configuration.validateSaveRestoreSupport() - print("pausing VM to take a snapshot...") - try await vm!.virtualMachine.pause() + print("pausing VM to take a snapshot...") + try await vm!.virtualMachine.pause() - print("creating a snapshot...") - try await vm!.virtualMachine.saveMachineStateTo(url: vmDir.stateURL) + print("creating a snapshot...") + try await vm!.virtualMachine.saveMachineStateTo(url: vmDir.stateURL) - print("snapshot created successfully! shutting down the VM...") + print("snapshot created successfully! shutting down the VM...") - task.cancel() - } else { - print(RuntimeError.SuspendFailed("this functionality is only supported on macOS 14 (Sonoma) or newer")) + task.cancel() + } else { + print(RuntimeError.SuspendFailed("this functionality is only supported on macOS 14 (Sonoma) or newer")) - Foundation.exit(1) - } + Foundation.exit(1) + } #endif } catch (let e) { print(RuntimeError.SuspendFailed(e.localizedDescription)) diff --git a/Sources/tart/Platform/Darwin.swift b/Sources/tart/Platform/Darwin.swift index 6f2fa85..732342c 100644 --- a/Sources/tart/Platform/Darwin.swift +++ b/Sources/tart/Platform/Darwin.swift @@ -8,129 +8,129 @@ struct UnsupportedHostOSError: Error, CustomStringConvertible { #if arch(arm64) -struct Darwin: PlatformSuspendable { - var ecid: VZMacMachineIdentifier - var hardwareModel: VZMacHardwareModel + struct Darwin: PlatformSuspendable { + var ecid: VZMacMachineIdentifier + var hardwareModel: VZMacHardwareModel - init(ecid: VZMacMachineIdentifier, hardwareModel: VZMacHardwareModel) { - self.ecid = ecid - self.hardwareModel = hardwareModel - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - let encodedECID = try container.decode(String.self, forKey: .ecid) - guard let data = Data.init(base64Encoded: encodedECID) else { - throw DecodingError.dataCorruptedError(forKey: .ecid, - in: container, - debugDescription: "failed to initialize Data using the provided value") - } - guard let ecid = VZMacMachineIdentifier.init(dataRepresentation: data) else { - throw DecodingError.dataCorruptedError(forKey: .ecid, - in: container, - debugDescription: "failed to initialize VZMacMachineIdentifier using the provided value") - } - self.ecid = ecid - - let encodedHardwareModel = try container.decode(String.self, forKey: .hardwareModel) - guard let data = Data.init(base64Encoded: encodedHardwareModel) else { - throw DecodingError.dataCorruptedError(forKey: .hardwareModel, in: container, debugDescription: "") - } - guard let hardwareModel = VZMacHardwareModel.init(dataRepresentation: data) else { - throw DecodingError.dataCorruptedError(forKey: .hardwareModel, in: container, debugDescription: "") - } - self.hardwareModel = hardwareModel - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(ecid.dataRepresentation.base64EncodedString(), forKey: .ecid) - try container.encode(hardwareModel.dataRepresentation.base64EncodedString(), forKey: .hardwareModel) - } - - func os() -> OS { - .darwin - } - - func bootLoader(nvramURL: URL) throws -> VZBootLoader { - VZMacOSBootLoader() - } - - func platform(nvramURL: URL) throws -> VZPlatformConfiguration { - let result = VZMacPlatformConfiguration() - - result.machineIdentifier = ecid - result.auxiliaryStorage = VZMacAuxiliaryStorage(url: nvramURL) - - if !hardwareModel.isSupported { - // At the moment support of M1 chip is not yet dropped in any macOS version - // This mean that host software is not supporting this hardware model and should be updated - throw UnsupportedHostOSError() + init(ecid: VZMacMachineIdentifier, hardwareModel: VZMacHardwareModel) { + self.ecid = ecid + self.hardwareModel = hardwareModel } - result.hardwareModel = hardwareModel + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) - return result - } + let encodedECID = try container.decode(String.self, forKey: .ecid) + guard let data = Data.init(base64Encoded: encodedECID) else { + throw DecodingError.dataCorruptedError(forKey: .ecid, + in: container, + debugDescription: "failed to initialize Data using the provided value") + } + guard let ecid = VZMacMachineIdentifier.init(dataRepresentation: data) else { + throw DecodingError.dataCorruptedError(forKey: .ecid, + in: container, + debugDescription: "failed to initialize VZMacMachineIdentifier using the provided value") + } + self.ecid = ecid - func graphicsDevice(vmConfig: VMConfig) -> VZGraphicsDeviceConfiguration { - let result = VZMacGraphicsDeviceConfiguration() + let encodedHardwareModel = try container.decode(String.self, forKey: .hardwareModel) + guard let data = Data.init(base64Encoded: encodedHardwareModel) else { + throw DecodingError.dataCorruptedError(forKey: .hardwareModel, in: container, debugDescription: "") + } + guard let hardwareModel = VZMacHardwareModel.init(dataRepresentation: data) else { + throw DecodingError.dataCorruptedError(forKey: .hardwareModel, in: container, debugDescription: "") + } + self.hardwareModel = hardwareModel + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(ecid.dataRepresentation.base64EncodedString(), forKey: .ecid) + try container.encode(hardwareModel.dataRepresentation.base64EncodedString(), forKey: .hardwareModel) + } + + func os() -> OS { + .darwin + } + + func bootLoader(nvramURL: URL) throws -> VZBootLoader { + VZMacOSBootLoader() + } + + func platform(nvramURL: URL) throws -> VZPlatformConfiguration { + let result = VZMacPlatformConfiguration() + + result.machineIdentifier = ecid + result.auxiliaryStorage = VZMacAuxiliaryStorage(url: nvramURL) + + if !hardwareModel.isSupported { + // At the moment support of M1 chip is not yet dropped in any macOS version + // This mean that host software is not supporting this hardware model and should be updated + throw UnsupportedHostOSError() + } + + result.hardwareModel = hardwareModel + + return result + } + + func graphicsDevice(vmConfig: VMConfig) -> VZGraphicsDeviceConfiguration { + let result = VZMacGraphicsDeviceConfiguration() + + if let hostMainScreen = NSScreen.main { + let vmScreenSize = NSSize(width: vmConfig.display.width, height: vmConfig.display.height) + result.displays = [ + VZMacGraphicsDisplayConfiguration(for: hostMainScreen, sizeInPoints: vmScreenSize) + ] + + return result + } - if let hostMainScreen = NSScreen.main { - let vmScreenSize = NSSize(width: vmConfig.display.width, height: vmConfig.display.height) result.displays = [ - VZMacGraphicsDisplayConfiguration(for: hostMainScreen, sizeInPoints: vmScreenSize) + VZMacGraphicsDisplayConfiguration( + widthInPixels: vmConfig.display.width, + heightInPixels: vmConfig.display.height, + // A reasonable guess according to Apple's documentation[1] + // [1]: https://developer.apple.com/documentation/coregraphics/1456599-cgdisplayscreensize + pixelsPerInch: 72 + ) ] return result } - result.displays = [ - VZMacGraphicsDisplayConfiguration( - widthInPixels: vmConfig.display.width, - heightInPixels: vmConfig.display.height, - // A reasonable guess according to Apple's documentation[1] - // [1]: https://developer.apple.com/documentation/coregraphics/1456599-cgdisplayscreensize - pixelsPerInch: 72 - ) - ] + func keyboards() -> [VZKeyboardConfiguration] { + if #available(macOS 14, *) { + // Mac keyboard is only supported by guests starting with macOS Ventura + return [VZMacKeyboardConfiguration(), VZUSBKeyboardConfiguration()] + } else { + return [VZUSBKeyboardConfiguration()] + } + } - return result - } + func keyboardsSuspendable() -> [VZKeyboardConfiguration] { + if #available(macOS 14, *) { + return [VZMacKeyboardConfiguration()] + } else { + // fallback to the regular configuration + return keyboards() + } + } - func keyboards() -> [VZKeyboardConfiguration] { - if #available(macOS 14, *) { - // Mac keyboard is only supported by guests starting with macOS Ventura - return [VZMacKeyboardConfiguration(), VZUSBKeyboardConfiguration()] - } else { - return [VZUSBKeyboardConfiguration()] + func pointingDevices() -> [VZPointingDeviceConfiguration] { + // Trackpad is only supported by guests starting with macOS Ventura + [VZMacTrackpadConfiguration(), VZUSBScreenCoordinatePointingDeviceConfiguration()] + } + + func pointingDevicesSuspendable() -> [VZPointingDeviceConfiguration] { + if #available(macOS 14, *) { + return [VZMacTrackpadConfiguration()] + } else { + // fallback to the regular configuration + return pointingDevices() + } } } - func keyboardsSuspendable() -> [VZKeyboardConfiguration] { - if #available(macOS 14, *) { - return [VZMacKeyboardConfiguration()] - } else { - // fallback to the regular configuration - return keyboards() - } - } - - func pointingDevices() -> [VZPointingDeviceConfiguration] { - // Trackpad is only supported by guests starting with macOS Ventura - [VZMacTrackpadConfiguration(), VZUSBScreenCoordinatePointingDeviceConfiguration()] - } - - func pointingDevicesSuspendable() -> [VZPointingDeviceConfiguration] { - if #available(macOS 14, *) { - return [VZMacTrackpadConfiguration()] - } else { - // fallback to the regular configuration - return pointingDevices() - } - } -} - #endif diff --git a/Sources/tart/VM.swift b/Sources/tart/VM.swift index 52d7499..901d113 100644 --- a/Sources/tart/VM.swift +++ b/Sources/tart/VM.swift @@ -126,82 +126,82 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { } #if arch(arm64) - init( - vmDir: VMDirectory, - ipswURL: URL, - diskSizeGB: UInt16, - network: Network = NetworkShared(), - additionalStorageDevices: [VZStorageDeviceConfiguration] = [], - directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [], - serialPorts: [VZSerialPortConfiguration] = [] - ) async throws { - var ipswURL = ipswURL + init( + vmDir: VMDirectory, + ipswURL: URL, + diskSizeGB: UInt16, + network: Network = NetworkShared(), + additionalStorageDevices: [VZStorageDeviceConfiguration] = [], + directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [], + serialPorts: [VZSerialPortConfiguration] = [] + ) async throws { + var ipswURL = ipswURL - if !ipswURL.isFileURL { - ipswURL = try await VM.retrieveIPSW(remoteURL: ipswURL) - } - - // We create a temporary TART_HOME directory in tests, which has its "cache" folder symlinked - // to the users Tart cache directory (~/.tart/cache). However, the Virtualization.Framework - // cannot deal with paths that contain symlinks, so expand them here first. - ipswURL.resolveSymlinksInPath() - - // Load the restore image and try to get the requirements - // that match both the image and our platform - let image = try await withCheckedThrowingContinuation { continuation in - VZMacOSRestoreImage.load(from: ipswURL) { result in - continuation.resume(with: result) + if !ipswURL.isFileURL { + ipswURL = try await VM.retrieveIPSW(remoteURL: ipswURL) } - } - guard let requirements = image.mostFeaturefulSupportedConfiguration else { - throw UnsupportedRestoreImageError() - } + // We create a temporary TART_HOME directory in tests, which has its "cache" folder symlinked + // to the users Tart cache directory (~/.tart/cache). However, the Virtualization.Framework + // cannot deal with paths that contain symlinks, so expand them here first. + ipswURL.resolveSymlinksInPath() - // Create NVRAM - _ = try VZMacAuxiliaryStorage(creatingStorageAt: vmDir.nvramURL, hardwareModel: requirements.hardwareModel) - - // Create disk - try vmDir.resizeDisk(diskSizeGB) - - name = vmDir.name - // Create config - config = VMConfig( - platform: Darwin(ecid: VZMacMachineIdentifier(), hardwareModel: requirements.hardwareModel), - cpuCountMin: requirements.minimumSupportedCPUCount, - memorySizeMin: requirements.minimumSupportedMemorySize - ) - // allocate at least 4 CPUs because otherwise VMs are frequently freezing - try config.setCPU(cpuCount: max(4, requirements.minimumSupportedCPUCount)) - try config.save(toURL: vmDir.configURL) - - // Initialize the virtual machine and its configuration - self.network = network - configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL, nvramURL: vmDir.nvramURL, - vmConfig: config, network: network, - additionalStorageDevices: additionalStorageDevices, - directorySharingDevices: directorySharingDevices, - serialPorts: serialPorts - ) - virtualMachine = VZVirtualMachine(configuration: configuration) - - super.init() - virtualMachine.delegate = self - - // Run automated installation - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - DispatchQueue.main.async { [ipswURL] in - let installer = VZMacOSInstaller(virtualMachine: self.virtualMachine, restoringFromImageAt: ipswURL) - - defaultLogger.appendNewLine("Installing OS...") - ProgressObserver(installer.progress).log(defaultLogger) - - installer.install { result in + // Load the restore image and try to get the requirements + // that match both the image and our platform + let image = try await withCheckedThrowingContinuation { continuation in + VZMacOSRestoreImage.load(from: ipswURL) { result in continuation.resume(with: result) } } + + guard let requirements = image.mostFeaturefulSupportedConfiguration else { + throw UnsupportedRestoreImageError() + } + + // Create NVRAM + _ = try VZMacAuxiliaryStorage(creatingStorageAt: vmDir.nvramURL, hardwareModel: requirements.hardwareModel) + + // Create disk + try vmDir.resizeDisk(diskSizeGB) + + name = vmDir.name + // Create config + config = VMConfig( + platform: Darwin(ecid: VZMacMachineIdentifier(), hardwareModel: requirements.hardwareModel), + cpuCountMin: requirements.minimumSupportedCPUCount, + memorySizeMin: requirements.minimumSupportedMemorySize + ) + // allocate at least 4 CPUs because otherwise VMs are frequently freezing + try config.setCPU(cpuCount: max(4, requirements.minimumSupportedCPUCount)) + try config.save(toURL: vmDir.configURL) + + // Initialize the virtual machine and its configuration + self.network = network + configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL, nvramURL: vmDir.nvramURL, + vmConfig: config, network: network, + additionalStorageDevices: additionalStorageDevices, + directorySharingDevices: directorySharingDevices, + serialPorts: serialPorts + ) + virtualMachine = VZVirtualMachine(configuration: configuration) + + super.init() + virtualMachine.delegate = self + + // Run automated installation + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + DispatchQueue.main.async { [ipswURL] in + let installer = VZMacOSInstaller(virtualMachine: self.virtualMachine, restoringFromImageAt: ipswURL) + + defaultLogger.appendNewLine("Installing OS...") + ProgressObserver(installer.progress).log(defaultLogger) + + installer.install { result in + continuation.resume(with: result) + } + } + } } - } #endif @available(macOS 13, *) @@ -248,11 +248,11 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { @MainActor private func start(_ recovery: Bool) async throws { #if arch(arm64) - let startOptions = VZMacOSVirtualMachineStartOptions() - startOptions.startUpFromMacOSRecovery = recovery - try await virtualMachine.start(options: startOptions) + let startOptions = VZMacOSVirtualMachineStartOptions() + startOptions.startUpFromMacOSRecovery = recovery + try await virtualMachine.start(options: startOptions) #else - try await virtualMachine.start() + try await virtualMachine.start() #endif } diff --git a/Sources/tart/VMConfig.swift b/Sources/tart/VMConfig.swift index b0d192c..c70e07a 100644 --- a/Sources/tart/VMConfig.swift +++ b/Sources/tart/VMConfig.swift @@ -96,12 +96,12 @@ struct VMConfig: Codable { switch os { case .darwin: #if arch(arm64) - platform = try Darwin(from: decoder) + platform = try Darwin(from: decoder) #else - throw DecodingError.dataCorruptedError( - forKey: .os, - in: container, - debugDescription: "Darwin VMs are only supported on Apple Silicon hosts") + throw DecodingError.dataCorruptedError( + forKey: .os, + in: container, + debugDescription: "Darwin VMs are only supported on Apple Silicon hosts") #endif case .linux: platform = try Linux(from: decoder)