Skip to content

Commit

Permalink
Add a download runtimes command
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenSorial committed Jan 20, 2023
1 parent 0a4cc40 commit d8b4e4c
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 37 deletions.
8 changes: 8 additions & 0 deletions Sources/XcodesKit/Downloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ public enum Downloader {
case urlSession
case aria2(Path)

public init(aria2Path: String?) {
guard let aria2Path = aria2Path.flatMap(Path.init) ?? Current.shell.findExecutable("aria2c"), aria2Path.exists else {
self = .urlSession
return
}
self = .aria2(aria2Path)
}

func download(url: URL, to destination: Path, progressChanged: @escaping (Progress) -> Void) -> Promise<URL> {
switch self {
case .urlSession:
Expand Down
24 changes: 18 additions & 6 deletions Sources/XcodesKit/RuntimeInstaller.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public class RuntimeInstaller {
str += " (\(runtime.build))"
}
if runtime.state == .legacyDownload || runtime.state == .diskImage {
str += " (Downloaded)"
str += " (Installed)"
} else if runtime.state == .bundled {
str += " (Bundled with selected Xcode)"
}
Expand All @@ -92,11 +92,15 @@ 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)")
}

public func downloadRuntime(identifier: String, to destinationDirectory: Path, with downloader: Downloader) async throws {
let matchedRuntime = try await getMatchingRuntime(identifier: identifier)

_ = try await downloadOrUseExistingArchive(runtime: matchedRuntime, to: destinationDirectory, downloader: downloader)
}


public func downloadAndInstallRuntime(identifier: String, to destinationDirectory: Path, with downloader: Downloader, shouldDelete: Bool) async throws {
let downloadables = try await runtimeList.downloadableRuntimes().downloadables
guard let matchedRuntime = downloadables.first(where: { $0.visibleIdentifier == identifier || $0.simulatorVersion.buildUpdate == identifier }) else {
throw Error.unavailableRuntime(identifier)
}
let matchedRuntime = try await getMatchingRuntime(identifier: identifier)

if matchedRuntime.contentType == .package && !Current.shell.isRoot() {
throw Error.rootNeeded
Expand All @@ -115,6 +119,14 @@ public class RuntimeInstaller {
}
}

private func getMatchingRuntime(identifier: String) async throws -> DownloadableRuntime {
let downloadables = try await runtimeList.downloadableRuntimes().downloadables
guard let runtime = downloadables.first(where: { $0.visibleIdentifier == identifier || $0.simulatorVersion.buildUpdate == identifier }) else {
throw Error.unavailableRuntime(identifier)
}
return runtime
}

private func installFromImage(dmgUrl: URL) async throws {
Current.logging.log("Installing Runtime")
try await Current.shell.installRuntimeImage(dmgUrl).asVoid().async()
Expand Down Expand Up @@ -187,7 +199,7 @@ public class RuntimeInstaller {
}

if Current.files.fileExistsAtPath(destination.string), aria2DownloadIsIncomplete == false {
Current.logging.log("Found existing Runtime that will be used for installation at \(destination).")
Current.logging.log("Found existing Runtime that will be used, at \(destination).")
return destination.url
}
if runtime.authentication == .virtual {
Expand Down
58 changes: 39 additions & 19 deletions Sources/xcodes/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,7 @@ struct Xcodes: AsyncParsableCommand {
installation = .version(versionString)
}

var downloader = Downloader.urlSession
if let aria2Path = aria2.flatMap(Path.init) ?? Current.shell.findExecutable("aria2c"),
aria2Path.exists,
noAria2 == false {
downloader = .aria2(aria2Path)
}
let downloader = noAria2 ? Downloader.urlSession : Downloader(aria2Path: aria2)

let destination = getDirectory(possibleDirectory: directory, default: .environmentDownloads)

Expand Down Expand Up @@ -241,12 +236,7 @@ struct Xcodes: AsyncParsableCommand {
installation = .version(versionString)
}

var downloader = Downloader.urlSession
if let aria2Path = aria2.flatMap(Path.init) ?? Current.shell.findExecutable("aria2c"),
aria2Path.exists,
noAria2 == false {
downloader = .aria2(aria2Path)
}
let downloader = noAria2 ? Downloader.urlSession : Downloader(aria2Path: aria2)

let destination = getDirectory(possibleDirectory: directory)

Expand Down Expand Up @@ -368,7 +358,7 @@ struct Xcodes: AsyncParsableCommand {
struct Runtimes: AsyncParsableCommand {
static var configuration = CommandConfiguration(
abstract: "List all simulator runtimes that are available to install",
subcommands: [Install.self]
subcommands: [Download.self, Install.self]
)

@Flag(help: "Include beta runtimes available to install")
Expand Down Expand Up @@ -406,19 +396,49 @@ struct Xcodes: AsyncParsableCommand {
func run() async throws {
Rainbow.enabled = Rainbow.enabled && globalColor.color

var downloader = Downloader.urlSession
if let aria2Path = aria2.flatMap(Path.init) ?? Current.shell.findExecutable("aria2c"),
aria2Path.exists,
noAria2 == false {
downloader = .aria2(aria2Path)
}
let downloader = noAria2 ? Downloader.urlSession : Downloader(aria2Path: aria2)

let destination = getDirectory(possibleDirectory: directory, default: .environmentDownloads)

try await runtimeInstaller.downloadAndInstallRuntime(identifier: version, to: destination, with: downloader, shouldDelete: !keepArchive)
Current.logging.log("Finished")
}
}

struct Download: AsyncParsableCommand {
static var configuration = CommandConfiguration(
abstract: "Download a specific simulator runtime"
)

@Argument(help: "The runtime to download")
var version: String

@Option(help: "The path to an aria2 executable. Searches $PATH by default.",
completion: .file())
var aria2: String?

@Flag(help: "Don't use aria2 to download the runtime, even if its available.")
var noAria2: Bool = false

@Option(help: "The directory to download the runtime archive to. Defaults to ~/Downloads.",
completion: .directory)
var directory: String?

@OptionGroup
var globalColor: GlobalColorOption

func run() async throws {
Rainbow.enabled = Rainbow.enabled && globalColor.color

let downloader = noAria2 ? Downloader.urlSession : Downloader(aria2Path: aria2)

let destination = getDirectory(possibleDirectory: directory, default: .environmentDownloads)

try await runtimeInstaller.downloadRuntime(identifier: version, to: destination, with: downloader)
Current.logging.log("Finished")
}
}

}

struct Select: ParsableCommand {
Expand Down
12 changes: 6 additions & 6 deletions Tests/XcodesKitTests/Fixtures/LogOutput-Runtime_NoBetas.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- iOS --
iOS 12.4 (Downloaded)
iOS 13.0 (Downloaded)
iOS 13.1 (Downloaded)
iOS 12.4 (Installed)
iOS 13.0 (Installed)
iOS 13.1 (Installed)
iOS 13.2.2
iOS 13.3
iOS 13.4
Expand All @@ -18,7 +18,7 @@ iOS 15.0
iOS 15.2
iOS 15.4
iOS 15.5 (Bundled with selected Xcode)
iOS 15.5 (Downloaded)
iOS 15.5 (Installed)
iOS 16.0
-- watchOS --
watchOS 6.0
Expand All @@ -31,9 +31,9 @@ watchOS 7.4
watchOS 8.0
watchOS 8.3
watchOS 8.5 (Bundled with selected Xcode)
watchOS 9.0-beta4 (Downloaded)
watchOS 9.0-beta4 (Installed)
watchOS 9.0 (20R362)
watchOS 9.0 (UnknownBuildNumber) (Downloaded)
watchOS 9.0 (UnknownBuildNumber) (Installed)
-- tvOS --
tvOS 12.4
tvOS 13.0
Expand Down
12 changes: 6 additions & 6 deletions Tests/XcodesKitTests/Fixtures/LogOutput-Runtimes.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- iOS --
iOS 12.4 (Downloaded)
iOS 13.0 (Downloaded)
iOS 13.1 (Downloaded)
iOS 12.4 (Installed)
iOS 13.0 (Installed)
iOS 13.1 (Installed)
iOS 13.2.2
iOS 13.3
iOS 13.4
Expand All @@ -18,7 +18,7 @@ iOS 15.0
iOS 15.2
iOS 15.4
iOS 15.5 (Bundled with selected Xcode)
iOS 15.5 (Downloaded)
iOS 15.5 (Installed)
iOS 16.0
-- watchOS --
watchOS 6.0
Expand All @@ -34,10 +34,10 @@ watchOS 8.5 (Bundled with selected Xcode)
watchOS 9.0-beta1
watchOS 9.0-beta2
watchOS 9.0-beta3
watchOS 9.0-beta4 (Downloaded)
watchOS 9.0-beta4 (Installed)
watchOS 9.0-beta5
watchOS 9.0 (20R362)
watchOS 9.0 (UnknownBuildNumber) (Downloaded)
watchOS 9.0 (UnknownBuildNumber) (Installed)
watchOS 9.1-beta1
-- tvOS --
tvOS 12.4
Expand Down

0 comments on commit d8b4e4c

Please sign in to comment.