Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Sources/XcodesKit/XcodeInstaller.swift
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,21 @@ public final class XcodeInstaller {
}
}

public func printXcodePath(ofVersion versionString: String, searchingIn directory: Path) -> Promise<Void> {
return firstly { () -> Promise<Void> in
guard let version = Version(xcodeVersion: versionString) else {
throw Error.invalidVersion(versionString)
}
let installedXcodes = Current.files.installedXcodes(directory)
.sorted { $0.version < $1.version }
guard let installedXcode = installedXcodes.first(withVersion: version) else {
throw Error.versionNotInstalled(version)
}
Current.logging.log(installedXcode.path.string)
return Promise.value(())
}
}

func unarchiveAndMoveXIP(at source: URL, to destination: URL, experimentalUnxip: Bool) -> Promise<URL> {
return firstly { () -> Promise<Void> in
Current.logging.log(InstallationStep.unarchiving(experimentalUnxip: experimentalUnxip).description)
Expand Down
16 changes: 14 additions & 2 deletions Sources/xcodes/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ struct Xcodes: ParsableCommand {
static var configuration = CommandConfiguration(
abstract: "List the versions of Xcode that are installed"
)

@Argument(help: "The version installed to which to print the path for",
completion: .custom { _ in Current.files.installedXcodes(getDirectory(possibleDirectory: nil)).sorted { $0.version < $1.version }.map { $0.version.appleDescription } })
var version: [String] = []

@OptionGroup
var globalDirectory: GlobalDirectoryOption
Expand All @@ -252,8 +256,16 @@ struct Xcodes: ParsableCommand {
Rainbow.enabled = Rainbow.enabled && globalColor.color

let directory = getDirectory(possibleDirectory: globalDirectory.directory)

installer.printInstalledXcodes(directory: directory)

installer.printXcodePath(ofVersion: version.joined(separator: " "), searchingIn: directory)
.recover { error -> Promise<Void> in
switch error {
case XcodeInstaller.Error.invalidVersion:
return installer.printInstalledXcodes(directory: directory)
default:
throw error
}
}
.done { Installed.exit() }
.catch { error in Installed.exit(withLegibleError: error) }

Expand Down
78 changes: 78 additions & 0 deletions Tests/XcodesKitTests/XcodesKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,84 @@ final class XcodesKitTests: XCTestCase {
"""
)
}

func test_Installed_WithValidVersion_PrintsXcodePath() {
var log = ""
XcodesKit.Current.logging.log = { log.append($0 + "\n") }

// There are installed Xcodes
Current.files.contentsAtPath = { path in
if path == "/Applications/Xcode-2.0.0.app/Contents/Info.plist" {
let url = Bundle.module.url(forResource: "Stub-2.0.0.Info", withExtension: "plist", subdirectory: "Fixtures")!
return try? Data(contentsOf: url)
}
else if path.contains("version.plist") {
let url = Bundle.module.url(forResource: "Stub.version", withExtension: "plist", subdirectory: "Fixtures")!
return try? Data(contentsOf: url)
}
else {
return nil
}
}
let installedXcodes = [
InstalledXcode(path: Path("/Applications/Xcode-2.0.0.app")!)!,
]
Current.files.installedXcodes = { _ in installedXcodes }

// One is selected
Current.shell.xcodeSelectPrintPath = {
Promise.value((status: 0, out: "/Applications/Xcode-2.0.0.app/Contents/Developer", err: ""))
}

// Standard output is not an interactive terminal
Current.shell.isatty = { false }

installer.printXcodePath(ofVersion: "2", searchingIn: Path.root/"Applications")
.cauterize()

XCTAssertEqual(
log,
"""
/Applications/Xcode-2.0.0.app

"""
)
}

func test_Installed_WithUninstalledVersion_ThrowsError() {
var log = ""
XcodesKit.Current.logging.log = { log.append($0 + "\n") }

// There are installed Xcodes
Current.files.contentsAtPath = { path in
if path == "/Applications/Xcode-2.0.0.app/Contents/Info.plist" {
let url = Bundle.module.url(forResource: "Stub-2.0.0.Info", withExtension: "plist", subdirectory: "Fixtures")!
return try? Data(contentsOf: url)
}
else if path.contains("version.plist") {
let url = Bundle.module.url(forResource: "Stub.version", withExtension: "plist", subdirectory: "Fixtures")!
return try? Data(contentsOf: url)
}
else {
return nil
}
}
let installedXcodes = [
InstalledXcode(path: Path("/Applications/Xcode-2.0.0.app")!)!,
]
Current.files.installedXcodes = { _ in installedXcodes }

// One is selected
Current.shell.xcodeSelectPrintPath = {
Promise.value((status: 0, out: "/Applications/Xcode-2.0.0.app/Contents/Developer", err: ""))
}

// Standard output is not an interactive terminal
Current.shell.isatty = { false }

installer.printXcodePath(ofVersion: "3", searchingIn: Path.root/"Applications")
.catch { error in XCTAssertEqual(error as! XcodeInstaller.Error, XcodeInstaller.Error.versionNotInstalled(Version(xcodeVersion: "3")!)) }
}

func test_Signout_WithExistingSession() {
var keychainDidRemove = false
Expand Down