diff --git a/Sources/XcodesKit/Models+Runtimes.swift b/Sources/XcodesKit/Models+Runtimes.swift index 6463569..66f0636 100644 --- a/Sources/XcodesKit/Models+Runtimes.swift +++ b/Sources/XcodesKit/Models+Runtimes.swift @@ -11,7 +11,7 @@ struct DownloadableRuntimesResponse: Decodable { public struct DownloadableRuntime: Decodable { let category: Category let simulatorVersion: SimulatorVersion - let source: String + let source: String? let dictionaryVersion: Int let contentType: ContentType let platform: Platform @@ -79,6 +79,7 @@ extension DownloadableRuntime { enum ContentType: String, Decodable { case diskImage = "diskImage" case package = "package" + case cryptexDiskImage = "cryptexDiskImage" } enum Platform: String, Decodable { diff --git a/Sources/XcodesKit/RuntimeInstaller.swift b/Sources/XcodesKit/RuntimeInstaller.swift index cf0821c..34189e8 100644 --- a/Sources/XcodesKit/RuntimeInstaller.swift +++ b/Sources/XcodesKit/RuntimeInstaller.swift @@ -112,6 +112,8 @@ public class RuntimeInstaller { try await installFromPackage(dmgUrl: dmgUrl, runtime: matchedRuntime) case .diskImage: try await installFromImage(dmgUrl: dmgUrl) + case .cryptexDiskImage: + throw Error.unsupportedCryptexDiskImage } if shouldDelete { Current.logging.log("Deleting Archive") @@ -183,7 +185,10 @@ public class RuntimeInstaller { @MainActor public func downloadOrUseExistingArchive(runtime: DownloadableRuntime, to destinationDirectory: Path, downloader: Downloader) async throws -> URL { - let url = URL(string: runtime.source)! + guard let source = runtime.source else { + throw Error.missingRuntimeSource(runtime.identifier) + } + let url = URL(string: source)! let destination = destinationDirectory/url.lastPathComponent let aria2DownloadMetadataPath = destination.parent/(destination.basename() + ".aria2") var aria2DownloadIsIncomplete = false @@ -226,6 +231,8 @@ extension RuntimeInstaller { case unavailableRuntime(String) case failedMountingDMG case rootNeeded + case missingRuntimeSource(String) + case unsupportedCryptexDiskImage public var errorDescription: String? { switch self { @@ -235,6 +242,10 @@ extension RuntimeInstaller { return "Failed to mount image." case .rootNeeded: return "Must be run as root to install the specified runtime" + case let .missingRuntimeSource(identifier): + return "Runtime \(identifier) is missing source url." + case .unsupportedCryptexDiskImage: + return "Cryptex Disk Image is not yet supported." } } } diff --git a/Tests/XcodesKitTests/Fixtures/DownloadableRuntimes.plist b/Tests/XcodesKitTests/Fixtures/DownloadableRuntimes.plist index 4229254..46d8a23 100644 --- a/Tests/XcodesKitTests/Fixtures/DownloadableRuntimes.plist +++ b/Tests/XcodesKitTests/Fixtures/DownloadableRuntimes.plist @@ -1794,7 +1794,34 @@ version 1.0.0.1 - + + category + simulator + contentType + cryptexDiskImage + dictionaryVersion + 2 + downloadMethod + mobileAsset + fileSize + 8455760175 + identifier + com.apple.dmg.iPhoneSimulatorSDK18_0_b1 + name + iOS 18.0 beta Simulator Runtime + platform + com.apple.platform.iphoneos + simulatorVersion + + buildUpdate + 22A5282m + version + 18.0 + + version + 18.0.0.1 + + refreshInterval 86400 sdkToSeedMappings diff --git a/Tests/XcodesKitTests/Fixtures/LogOutput-Runtimes.txt b/Tests/XcodesKitTests/Fixtures/LogOutput-Runtimes.txt index 591f086..cedde68 100644 --- a/Tests/XcodesKitTests/Fixtures/LogOutput-Runtimes.txt +++ b/Tests/XcodesKitTests/Fixtures/LogOutput-Runtimes.txt @@ -25,6 +25,7 @@ iOS 16.2 iOS 16.4 iOS 17.0-beta1 iOS 17.0-beta2 +iOS 18.0-beta1 -- watchOS -- watchOS 6.0 watchOS 6.1.1 diff --git a/Tests/XcodesKitTests/RuntimeTests.swift b/Tests/XcodesKitTests/RuntimeTests.swift index 314761a..04f6c89 100644 --- a/Tests/XcodesKitTests/RuntimeTests.swift +++ b/Tests/XcodesKitTests/RuntimeTests.swift @@ -80,7 +80,7 @@ final class RuntimeTests: XCTestCase { func test_downloadableRuntimes() async throws { mockDownloadables() let values = try await runtimeList.downloadableRuntimes().downloadables - XCTAssertEqual(values.count, 59) + XCTAssertEqual(values.count, 60) } func test_downloadableRuntimesNoBetas() async throws { @@ -171,7 +171,7 @@ final class RuntimeTests: XCTestCase { } let url = try await runtimeInstaller.downloadOrUseExistingArchive(runtime: runtime, to: .xcodesCaches, downloader: .urlSession) - let fileName = URL(string: runtime.source)!.lastPathComponent + let fileName = URL(string: runtime.source!)!.lastPathComponent XCTAssertEqual(url, Path.xcodesCaches.join(fileName).url) XCTAssertNil(xcodeDownloadURL) } @@ -185,10 +185,10 @@ final class RuntimeTests: XCTestCase { return (Progress(), Promise.value((destination, HTTPURLResponse(url: url.pmkRequest.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!))) } let runtime = try await runtimeList.downloadableRuntimes().downloadables.first { $0.visibleIdentifier == "iOS 15.5" }! - let fileName = URL(string: runtime.source)!.lastPathComponent + let fileName = URL(string: runtime.source!)!.lastPathComponent let url = try await runtimeInstaller.downloadOrUseExistingArchive(runtime: runtime, to: .xcodesCaches, downloader: .urlSession) XCTAssertEqual(url, Path.xcodesCaches.join(fileName).url) - XCTAssertEqual(xcodeDownloadURL, URL(string: runtime.source)!) + XCTAssertEqual(xcodeDownloadURL, URL(string: runtime.source!)!) } func test_installStepsForPackage() async throws {