diff --git a/Sources/XcodesKit/Models+Runtimes.swift b/Sources/XcodesKit/Models+Runtimes.swift index 7c3966a..f07dfa1 100644 --- a/Sources/XcodesKit/Models+Runtimes.swift +++ b/Sources/XcodesKit/Models+Runtimes.swift @@ -108,18 +108,18 @@ extension DownloadableRuntime { } public struct InstalledRuntime: Decodable { - let build: String + let build: String? // does not apply for unusable runtimes let deletable: Bool let identifier: UUID let kind: Kind let lastUsedAt: Date? let path: String - let platformIdentifier: Platform - let runtimeBundlePath: String - let runtimeIdentifier: String + let platformIdentifier: Platform? + let runtimeBundlePath: String? // does not apply for unusable runtimes + let runtimeIdentifier: String? // does not apply for unusable runtimes let signatureState: String let state: String - let version: String + let version: String? // does not apply for unusable runtimes let sizeBytes: Int? } diff --git a/Sources/XcodesKit/RuntimeInstaller.swift b/Sources/XcodesKit/RuntimeInstaller.swift index dd91799..624dcd3 100644 --- a/Sources/XcodesKit/RuntimeInstaller.swift +++ b/Sources/XcodesKit/RuntimeInstaller.swift @@ -38,18 +38,16 @@ public class RuntimeInstaller { } } - - - + let unusableCount = installed.removeAll { $0.state == "Unusable" }.count installed.forEach { runtime in let resolvedBetaNumber = downloadablesResponse.sdkToSeedMappings.first { - $0.buildUpdate == runtime.build + $0.buildUpdate == runtime.build! }?.seedNumber - var result = PrintableRuntime(platform: runtime.platformIdentifier.asPlatformOS, + var result = PrintableRuntime(platform: runtime.platformIdentifier!.asPlatformOS, betaNumber: resolvedBetaNumber, - version: runtime.version, - build: runtime.build, + version: runtime.version!, + build: runtime.build!, state: runtime.kind) mappedRuntimes.indices { @@ -90,6 +88,9 @@ public class RuntimeInstaller { } } Current.logging.log("\nNote: Bundled runtimes are indicated for the currently selected Xcode, more bundled runtimes may exist in other Xcode(s)") + if unusableCount > 0 { + Current.logging.log("Note: Found \(unusableCount) unknown downloaded runtime(s). For more info run `xcrun simctl runtime list -j`.") + } } public func downloadAndInstallRuntime(identifier: String, to destinationDirectory: Path, with downloader: Downloader, shouldDelete: Bool) async throws { diff --git a/Tests/XcodesKitTests/Fixtures/LogOutput-Runtimes_WithUnusable.txt b/Tests/XcodesKitTests/Fixtures/LogOutput-Runtimes_WithUnusable.txt new file mode 100644 index 0000000..747dcb5 --- /dev/null +++ b/Tests/XcodesKitTests/Fixtures/LogOutput-Runtimes_WithUnusable.txt @@ -0,0 +1,54 @@ +-- iOS -- +iOS 12.4 (Downloaded) +iOS 13.0 (Downloaded) +iOS 13.1 (Downloaded) +iOS 13.2.2 +iOS 13.3 +iOS 13.4 +iOS 13.5 +iOS 13.6 +iOS 13.7 +iOS 14.0.1 +iOS 14.1 +iOS 14.2 +iOS 14.3 +iOS 14.4 +iOS 14.5 +iOS 15.0 +iOS 15.2 +iOS 15.4 +iOS 15.5 (Bundled with selected Xcode) +iOS 15.5 (Downloaded) +iOS 16.0 +-- watchOS -- +watchOS 6.0 +watchOS 6.1.1 +watchOS 6.2.1 +watchOS 7.0 +watchOS 7.1 +watchOS 7.2 +watchOS 7.4 +watchOS 8.0 +watchOS 8.3 +watchOS 8.5 (Bundled with selected Xcode) +watchOS 9.0-beta4 (Downloaded) +watchOS 9.0 (20R362) +watchOS 9.0 (UnknownBuildNumber) (Downloaded) +-- tvOS -- +tvOS 12.4 +tvOS 13.0 +tvOS 13.2 +tvOS 13.3 +tvOS 13.4 +tvOS 14.0 +tvOS 14.2 +tvOS 14.3 +tvOS 14.4 +tvOS 14.5 +tvOS 15.0 +tvOS 15.2 +tvOS 15.4 (Bundled with selected Xcode) +tvOS 16.0 + +Note: Bundled runtimes are indicated for the currently selected Xcode, more bundled runtimes may exist in other Xcode(s) +Note: Found 1 unknown downloaded runtime(s). For more info run `xcrun simctl runtime list -j`. diff --git a/Tests/XcodesKitTests/Fixtures/ShellOutput-InstalledRuntimes_WithUnusable.json b/Tests/XcodesKitTests/Fixtures/ShellOutput-InstalledRuntimes_WithUnusable.json new file mode 100644 index 0000000..23e6337 --- /dev/null +++ b/Tests/XcodesKitTests/Fixtures/ShellOutput-InstalledRuntimes_WithUnusable.json @@ -0,0 +1,142 @@ +{ + "2A6068A0-7FCF-4DB9-964D-21145EB98498" : { + "build" : "17A844", + "deletable" : true, + "identifier" : "2A6068A0-7FCF-4DB9-964D-21145EB98498", + "kind" : "Legacy Download", + "lastUsedAt" : "2022-09-24T02:57:33Z", + "path" : "\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 13.1.simruntime", + "platformIdentifier" : "com.apple.platform.iphonesimulator", + "runtimeBundlePath" : "\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 13.1.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.iOS-13-1", + "signatureState" : "Unknown", + "sizeBytes" : 6355320832, + "state" : "Ready", + "version" : "13.1" + }, + "6DE6B631-9439-4737-A65B-73F675EB77D1" : { + "build" : "20R5332f", + "deletable" : true, + "identifier" : "6DE6B631-9439-4737-A65B-73F675EB77D1", + "kind" : "Disk Image", + "mountPath" : "\/Library\/Developer\/CoreSimulator\/Volumes\/watchOS_20R362", + "path" : "\/Library\/Developer\/CoreSimulator\/Images\/6DE6B631-9439-4737-A65B-73F675EB77D1.dmg", + "platformIdentifier" : "com.apple.platform.watchsimulator", + "runtimeBundlePath" : "\/Library\/Developer\/CoreSimulator\/Volumes\/watchOS_20R362\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/watchOS 9.0.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.watchOS-9-0", + "signatureState" : "Verified", + "sizeBytes" : 3604135917, + "state" : "Ready", + "version" : "9.0" + }, + "6DE6B631-9439-4737-A65B-73F675EB77D2" : { + "build" : "UnknownBuildNumber", + "deletable" : true, + "identifier" : "6DE6B631-9439-4737-A65B-73F675EB77D2", + "kind" : "Disk Image", + "mountPath" : "\/Library\/Developer\/CoreSimulator\/Volumes\/watchOS_20R362", + "path" : "\/Library\/Developer\/CoreSimulator\/Images\/6DE6B631-9439-4737-A65B-73F675EB77D1.dmg", + "platformIdentifier" : "com.apple.platform.watchsimulator", + "runtimeBundlePath" : "\/Library\/Developer\/CoreSimulator\/Volumes\/watchOS_20R362\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/watchOS 9.0.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.watchOS-9-0", + "signatureState" : "Verified", + "sizeBytes" : 3604135917, + "state" : "Ready", + "version" : "9.0" + }, + "7A032D54-0D93-4E04-80B9-4CB207136C3F" : { + "build" : "16G73", + "deletable" : true, + "identifier" : "7A032D54-0D93-4E04-80B9-4CB207136C3F", + "kind" : "Legacy Download", + "path" : "\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 12.4.simruntime", + "platformIdentifier" : "com.apple.platform.iphonesimulator", + "runtimeBundlePath" : "\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 12.4.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.iOS-12-4", + "signatureState" : "Unknown", + "sizeBytes" : 4980690944, + "state" : "Ready", + "version" : "12.4" + }, + "91B92361-CD02-4AF7-8DFE-DE8764AA949F" : { + "build" : "19F70", + "deletable" : false, + "identifier" : "91B92361-CD02-4AF7-8DFE-DE8764AA949F", + "kind" : "Bundled with Xcode", + "path" : "\/Applications\/Xcode-13.4.1.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime", + "platformIdentifier" : "com.apple.platform.iphonesimulator", + "runtimeBundlePath" : "\/Applications\/Xcode-13.4.1.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.iOS-15-5", + "signatureState" : "Unknown", + "sizeBytes" : 3310809088, + "state" : "Ready", + "version" : "15.5" + }, + "630146EA-A027-42B1-AC25-BE4EA018DE90" : { + "build" : "17A577", + "deletable" : true, + "identifier" : "630146EA-A027-42B1-AC25-BE4EA018DE90", + "kind" : "Legacy Download", + "path" : "\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 13.0.simruntime", + "platformIdentifier" : "com.apple.platform.iphonesimulator", + "runtimeBundlePath" : "\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 13.0.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.iOS-13-0", + "signatureState" : "Unknown", + "sizeBytes" : 6369886208, + "state" : "Ready", + "version" : "13.0" + }, + "AAD753FE-A798-479C-B6D6-41259B063DD6" : { + "build" : "19F70", + "deletable" : true, + "identifier" : "AAD753FE-A798-479C-B6D6-41259B063DD6", + "kind" : "Legacy Download", + "path" : "\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 15.5.simruntime", + "platformIdentifier" : "com.apple.platform.iphonesimulator", + "runtimeBundlePath" : "\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS 15.5.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.iOS-15-5", + "signatureState" : "Unknown", + "state" : "Ready", + "version" : "15.5" + }, + "BE68168B-7AC8-4A1F-A344-15DFCC375457" : { + "build" : "19L439", + "deletable" : false, + "identifier" : "BE68168B-7AC8-4A1F-A344-15DFCC375457", + "kind" : "Bundled with Xcode", + "path" : "\/Applications\/Xcode-13.4.1.app\/Contents\/Developer\/Platforms\/AppleTVOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/tvOS.simruntime", + "platformIdentifier" : "com.apple.platform.appletvsimulator", + "runtimeBundlePath" : "\/Applications\/Xcode-13.4.1.app\/Contents\/Developer\/Platforms\/AppleTVOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/tvOS.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.tvOS-15-4", + "signatureState" : "Unknown", + "sizeBytes" : 1615810560, + "state" : "Ready", + "version" : "15.4" + }, + "F8D81829-354C-4EB0-828D-83DC765B27E1" : { + "build" : "19T241", + "deletable" : false, + "identifier" : "F8D81829-354C-4EB0-828D-83DC765B27E1", + "kind" : "Bundled with Xcode", + "path" : "\/Applications\/Xcode-13.4.1.app\/Contents\/Developer\/Platforms\/WatchOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/watchOS.simruntime", + "platformIdentifier" : "com.apple.platform.watchsimulator", + "runtimeBundlePath" : "\/Applications\/Xcode-13.4.1.app\/Contents\/Developer\/Platforms\/WatchOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/watchOS.simruntime", + "runtimeIdentifier" : "com.apple.CoreSimulator.SimRuntime.watchOS-8-5", + "signatureState" : "Unknown", + "sizeBytes" : 1540558848, + "state" : "Ready", + "version" : "8.5" + }, + "820783CB-C389-4006-B2D2-D063F3FD10A1" : { + "deletable" : true, + "identifier" : "820783CB-C389-4006-B2D2-D063F3FD10A1", + "kind" : "Disk Image", + "path" : "\/Library\/Developer\/CoreSimulator\/Images\/Inbox\/820783CB-C389-4006-B2D2-D063F3FD10A1.dmg", + "signatureState" : "Missing", + "sizeBytes" : 5304795932, + "state" : "Unusable", + "unusableErrorDetail" : "Error Domain=SimDiskImageErrorDomain Code=3 \"Missing Signature\" UserInfo={NSLocalizedDescription=Missing Signature, unusableErrorDetail=}", + "unusableErrorMessage" : "Missing Signature", + "unusableSubstate" : "Missing Signature" + } +} diff --git a/Tests/XcodesKitTests/RuntimeTests.swift b/Tests/XcodesKitTests/RuntimeTests.swift index 2519138..9290f61 100644 --- a/Tests/XcodesKitTests/RuntimeTests.swift +++ b/Tests/XcodesKitTests/RuntimeTests.swift @@ -116,6 +116,19 @@ final class RuntimeTests: XCTestCase { XCTAssertEqual(log, try String(contentsOf: outputUrl)) } + func test_printAvailableRuntimes_WithUnusable() async throws { + var log = "" + XcodesKit.Current.logging.log = { log.append($0 + "\n") } + mockDownloadables() + Current.shell.installedRuntimes = { + let url = Bundle.module.url(forResource: "ShellOutput-InstalledRuntimes_WithUnusable", withExtension: "json", subdirectory: "Fixtures")! + return Promise.value((0, try! String(contentsOf: url), "")) + } + try await runtimeInstaller.printAvailableRuntimes(includeBetas: false) + let outputUrl = Bundle.module.url(forResource: "LogOutput-Runtimes_WithUnusable", withExtension: "txt", subdirectory: "Fixtures")! + XCTAssertEqual(log, try String(contentsOf: outputUrl)) + } + func test_wrongIdentifier() async throws { mockDownloadables() var resultError: RuntimeInstaller.Error? = nil