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 {