diff --git a/README.md b/README.md index fd28163..5acbb18 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,6 @@ xcodes will download and install the version you asked for so that it's ready to (5/6) Checking security assessment and code signing (6/6) Finishing installation xcodes requires superuser privileges in order to finish installation. -macOS User Password: Xcode 11.2.0 has been installed to /Applications/Xcode-11.2.0.app ``` diff --git a/Sources/XcodesKit/Environment.swift b/Sources/XcodesKit/Environment.swift index 393e19c..2f9d0c2 100644 --- a/Sources/XcodesKit/Environment.swift +++ b/Sources/XcodesKit/Environment.swift @@ -33,33 +33,22 @@ public struct Shell { public var installRuntimeImage: (URL) -> Promise = { Process.run(Path.root.usr.bin.join("xcrun"), "simctl", "runtime", "add", $0.path) } public var spctlAssess: (URL) -> Promise = { Process.run(Path.root.usr.sbin.spctl, "--assess", "--verbose", "--type", "execute", "\($0.path)") } public var codesignVerify: (URL) -> Promise = { Process.run(Path.root.usr.bin.codesign, "-vv", "-d", "\($0.path)") } - public var devToolsSecurityEnable: (String?) -> Promise = { Process.sudo(password: $0, Path.root.usr.sbin.DevToolsSecurity, "-enable") } - public var addStaffToDevelopersGroup: (String?) -> Promise = { Process.sudo(password: $0, Path.root.usr.sbin.dseditgroup, "-o", "edit", "-t", "group", "-a", "staff", "_developer") } - public var acceptXcodeLicense: (InstalledXcode, String?) -> Promise = { Process.sudo(password: $1, $0.path.join("/Contents/Developer/usr/bin/xcodebuild"), "-license", "accept") } - public var runFirstLaunch: (InstalledXcode, String?) -> Promise = { Process.sudo(password: $1, $0.path.join("/Contents/Developer/usr/bin/xcodebuild"),"-runFirstLaunch") } + public var devToolsSecurityEnable: () -> Promise = { Process.sudo(Path.root.usr.sbin.DevToolsSecurity, "-enable") } + public var addStaffToDevelopersGroup: () -> Promise = { Process.sudo(Path.root.usr.sbin.dseditgroup, "-o", "edit", "-t", "group", "-a", "staff", "_developer") } + public var acceptXcodeLicense: (InstalledXcode) -> Promise = { Process.sudo($0.path.join("/Contents/Developer/usr/bin/xcodebuild"), "-license", "accept") } + public var runFirstLaunch: (InstalledXcode) -> Promise = { Process.sudo($0.path.join("/Contents/Developer/usr/bin/xcodebuild"),"-runFirstLaunch") } public var buildVersion: () -> Promise = { Process.run(Path.root.usr.bin.sw_vers, "-buildVersion") } public var xcodeBuildVersion: (InstalledXcode) -> Promise = { Process.run(Path.root.usr.libexec.PlistBuddy, "-c", "Print :ProductBuildVersion", "\($0.path.string)/Contents/version.plist") } public var getUserCacheDir: () -> Promise = { Process.run(Path.root.usr.bin.getconf, "DARWIN_USER_CACHE_DIR") } public var touchInstallCheck: (String, String, String) -> Promise = { Process.run(Path.root.usr.bin/"touch", "\($0)com.apple.dt.Xcode.InstallCheckCache_\($1)_\($2)") } public var installedRuntimes: () -> Promise = { Process.run(Path.root.usr.bin.join("xcrun"), "simctl", "runtime", "list", "-j") } - public var validateSudoAuthentication: () -> Promise = { Process.run(Path.root.usr.bin.sudo, "-nv") } - public var authenticateSudoerIfNecessary: (@escaping () -> Promise) -> Promise = { passwordInput in - firstly { () -> Promise in - Current.shell.validateSudoAuthentication().map { _ in return nil } - } - .recover { _ -> Promise in - return passwordInput().map(Optional.init) - } - } - public func authenticateSudoerIfNecessary(passwordInput: @escaping () -> Promise) -> Promise { - authenticateSudoerIfNecessary(passwordInput) - } + public var authenticateSudoer: () -> Promise = { Process.run(Path.root.usr.bin.sudo, "-v") } public var xcodeSelectPrintPath: () -> Promise = { Process.run(Path.root.usr.bin.join("xcode-select"), "-p") } - public var xcodeSelectSwitch: (String?, String) -> Promise = { Process.sudo(password: $0, Path.root.usr.bin.join("xcode-select"), "-s", $1) } - public func xcodeSelectSwitch(password: String?, path: String) -> Promise { - xcodeSelectSwitch(password, path) + public var xcodeSelectSwitch: (String) -> Promise = { Process.sudo(Path.root.usr.bin.join("xcode-select"), "-s", $0) } + public func xcodeSelectSwitch(path: String) -> Promise { + xcodeSelectSwitch(path) } public var isRoot: () -> Bool = { NSUserName() == "root" } diff --git a/Sources/XcodesKit/Process.swift b/Sources/XcodesKit/Process.swift index dd79217..5ebc83d 100644 --- a/Sources/XcodesKit/Process.swift +++ b/Sources/XcodesKit/Process.swift @@ -7,12 +7,8 @@ public typealias ProcessOutput = (status: Int32, out: String, err: String) extension Process { @discardableResult - static func sudo(password: String? = nil, _ executable: Path, workingDirectory: URL? = nil, _ arguments: String...) -> Promise { - var arguments = [executable.string] + arguments - if password != nil { - arguments.insert("-S", at: 0) - } - return run(Path.root.usr.bin.sudo.url, workingDirectory: workingDirectory, input: password, arguments) + static func sudo(_ executable: Path, workingDirectory: URL? = nil, _ arguments: String...) -> Promise { + run(Path.root.usr.bin.sudo.url, workingDirectory: workingDirectory, [executable.string] + arguments) } @discardableResult diff --git a/Sources/XcodesKit/XcodeInstaller.swift b/Sources/XcodesKit/XcodeInstaller.swift index 44747c6..0b4b68f 100644 --- a/Sources/XcodesKit/XcodeInstaller.swift +++ b/Sources/XcodesKit/XcodeInstaller.swift @@ -19,7 +19,6 @@ public final class XcodeInstaller { case codesignVerifyFailed(output: String) case unexpectedCodeSigningIdentity(identifier: String, certificateAuthority: [String]) case unsupportedFileFormat(extension: String) - case missingSudoerPassword case unavailableVersion(Version) case noReleaseVersionAvailable case noPrereleaseVersionAvailable @@ -57,8 +56,6 @@ public final class XcodeInstaller { """ case .unsupportedFileFormat(let fileExtension): return "xcodes doesn't (yet) support installing Xcode from the \(fileExtension) file format." - case .missingSudoerPassword: - return "Missing password. Please try again." case let .unavailableVersion(version): return "Could not find version \(version.appleDescription)." case .noReleaseVersionAvailable: @@ -421,23 +418,20 @@ public final class XcodeInstaller { } public func postInstallXcode(_ xcode: InstalledXcode) -> Promise { - let passwordInput = { - Promise { seal in - Current.logging.log("xcodes requires superuser privileges in order to finish installation.") - guard let password = Current.shell.readSecureLine(prompt: "macOS User Password: ") else { seal.reject(Error.missingSudoerPassword); return } - seal.fulfill(password + "\n") - } - } return firstly { () -> Promise in Current.logging.log(InstallationStep.finishing.description) + Current.logging.log("xcodes requires superuser privileges in order to finish installation.") - return self.enableDeveloperMode(passwordInput: passwordInput).map { xcode } + return Current.shell.authenticateSudoer().asVoid().map { xcode } + } + .then { xcode -> Promise in + self.enableDeveloperMode().map { xcode } } .then { xcode -> Promise in - self.approveLicense(for: xcode, passwordInput: passwordInput).map { xcode } + self.approveLicense(for: xcode).map { xcode } } .then { xcode -> Promise in - self.installComponents(for: xcode, passwordInput: passwordInput).map { xcode } + self.installComponents(for: xcode).map { xcode } } } @@ -722,33 +716,22 @@ public final class XcodeInstaller { return info } - func enableDeveloperMode(passwordInput: @escaping () -> Promise) -> Promise { - return firstly { () -> Promise in - Current.shell.authenticateSudoerIfNecessary(passwordInput: passwordInput) + func enableDeveloperMode() -> Promise { + firstly { + Current.shell.devToolsSecurityEnable() } - .then { possiblePassword -> Promise in - return Current.shell.devToolsSecurityEnable(possiblePassword).map { _ in possiblePassword } - } - .then { possiblePassword in - return Current.shell.addStaffToDevelopersGroup(possiblePassword).asVoid() + .then { _ in + Current.shell.addStaffToDevelopersGroup().asVoid() } } - func approveLicense(for xcode: InstalledXcode, passwordInput: @escaping () -> Promise) -> Promise { - return firstly { () -> Promise in - Current.shell.authenticateSudoerIfNecessary(passwordInput: passwordInput) - } - .then { possiblePassword in - return Current.shell.acceptXcodeLicense(xcode, possiblePassword).asVoid() - } + func approveLicense(for xcode: InstalledXcode) -> Promise { + Current.shell.acceptXcodeLicense(xcode).asVoid() } - func installComponents(for xcode: InstalledXcode, passwordInput: @escaping () -> Promise) -> Promise { - return firstly { () -> Promise in - Current.shell.authenticateSudoerIfNecessary(passwordInput: passwordInput) - } - .then { possiblePassword -> Promise in - Current.shell.runFirstLaunch(xcode, possiblePassword).asVoid() + func installComponents(for xcode: InstalledXcode) -> Promise { + firstly { + Current.shell.runFirstLaunch(xcode).asVoid() } .then { () -> Promise<(String, String, String)> in return when(fulfilled: diff --git a/Sources/XcodesKit/XcodeSelect.swift b/Sources/XcodesKit/XcodeSelect.swift index 7a4c193..a119e9e 100644 --- a/Sources/XcodesKit/XcodeSelect.swift +++ b/Sources/XcodesKit/XcodeSelect.swift @@ -127,23 +127,16 @@ public func selectXcodeInteractively(currentPath: String, directory: Path) -> Pr } public func selectXcodeAtPath(_ pathString: String) -> Promise { - firstly { () -> Promise in + firstly { guard Current.files.fileExists(atPath: pathString) else { throw XcodeSelectError.invalidPath(pathString) } - let passwordInput = { - Promise { seal in - Current.logging.log("xcodes requires superuser privileges to select an Xcode") - guard let password = Current.shell.readSecureLine(prompt: "macOS User Password: ") else { seal.reject(XcodeInstaller.Error.missingSudoerPassword); return } - seal.fulfill(password + "\n") - } - } - - return Current.shell.authenticateSudoerIfNecessary(passwordInput: passwordInput) + Current.logging.log("xcodes requires superuser privileges to select an Xcode") + return Current.shell.authenticateSudoer().asVoid() } - .then { possiblePassword in - Current.shell.xcodeSelectSwitch(password: possiblePassword, path: pathString) + .then { + Current.shell.xcodeSelectSwitch(path: pathString) } .then { _ in Current.shell.xcodeSelectPrintPath() diff --git a/Tests/XcodesKitTests/Environment+Mock.swift b/Tests/XcodesKitTests/Environment+Mock.swift index 067f9ce..784e3c7 100644 --- a/Tests/XcodesKitTests/Environment+Mock.swift +++ b/Tests/XcodesKitTests/Environment+Mock.swift @@ -25,19 +25,18 @@ extension Shell { installRuntimeImage: { _ in return Promise.value(Shell.processOutputMock) }, spctlAssess: { _ in return Promise.value(Shell.processOutputMock) }, codesignVerify: { _ in return Promise.value(Shell.processOutputMock) }, - devToolsSecurityEnable: { _ in return Promise.value(Shell.processOutputMock) }, - addStaffToDevelopersGroup: { _ in return Promise.value(Shell.processOutputMock) }, - acceptXcodeLicense: { _, _ in return Promise.value(Shell.processOutputMock) }, - runFirstLaunch: { _, _ in return Promise.value(Shell.processOutputMock) }, + devToolsSecurityEnable: { return Promise.value(Shell.processOutputMock) }, + addStaffToDevelopersGroup: { return Promise.value(Shell.processOutputMock) }, + acceptXcodeLicense: { _ in return Promise.value(Shell.processOutputMock) }, + runFirstLaunch: { _ in return Promise.value(Shell.processOutputMock) }, buildVersion: { return Promise.value(Shell.processOutputMock) }, xcodeBuildVersion: { _ in return Promise.value(Shell.processOutputMock) }, getUserCacheDir: { return Promise.value(Shell.processOutputMock) }, touchInstallCheck: { _, _, _ in return Promise.value(Shell.processOutputMock) }, installedRuntimes: { return Promise.value(Shell.processOutputMock) }, - validateSudoAuthentication: { return Promise.value(Shell.processOutputMock) }, - // Deliberately using real implementation of authenticateSudoerIfNecessary since it depends on others that can be mocked + authenticateSudoer: { return Promise.value(Shell.processOutputMock) }, xcodeSelectPrintPath: { return Promise.value(Shell.processOutputMock) }, - xcodeSelectSwitch: { _, _ in return Promise.value(Shell.processOutputMock) }, + xcodeSelectSwitch: { _ in return Promise.value(Shell.processOutputMock) }, isRoot: { true }, readLine: { _ in return nil }, readSecureLine: { _, _ in return nil }, diff --git a/Tests/XcodesKitTests/Fixtures/LogOutput-AlternativeDirectory.txt b/Tests/XcodesKitTests/Fixtures/LogOutput-AlternativeDirectory.txt index 7e0d0f7..6bfc075 100644 --- a/Tests/XcodesKitTests/Fixtures/LogOutput-AlternativeDirectory.txt +++ b/Tests/XcodesKitTests/Fixtures/LogOutput-AlternativeDirectory.txt @@ -110,6 +110,5 @@ Using regular unxip. Try passing `--experimental-unxip` for a faster unxip proce (5/6) Checking security assessment and code signing (6/6) Finishing installation xcodes requires superuser privileges in order to finish installation. -macOS User Password: Xcode 0.0.0 has been installed to /Users/brandon/Xcode/Xcode-0.0.0.app diff --git a/Tests/XcodesKitTests/Fixtures/LogOutput-DamagedXIP.txt b/Tests/XcodesKitTests/Fixtures/LogOutput-DamagedXIP.txt index c3a0e12..e9e36e6 100644 --- a/Tests/XcodesKitTests/Fixtures/LogOutput-DamagedXIP.txt +++ b/Tests/XcodesKitTests/Fixtures/LogOutput-DamagedXIP.txt @@ -117,6 +117,5 @@ Using regular unxip. Try passing `--experimental-unxip` for a faster unxip proce (5/6) Checking security assessment and code signing (6/6) Finishing installation xcodes requires superuser privileges in order to finish installation. -macOS User Password: Xcode 0.0.0 has been installed to /Applications/Xcode-0.0.0.app diff --git a/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath-NoColor.txt b/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath-NoColor.txt index c2c1d68..95b83e6 100644 --- a/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath-NoColor.txt +++ b/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath-NoColor.txt @@ -110,6 +110,5 @@ Using regular unxip. Try passing `--experimental-unxip` for a faster unxip proce (5/6) Checking security assessment and code signing (6/6) Finishing installation xcodes requires superuser privileges in order to finish installation. -macOS User Password: Xcode 0.0.0 has been installed to /Applications/Xcode-0.0.0.app diff --git a/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath-NonInteractiveTerminal.txt b/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath-NonInteractiveTerminal.txt index e43b59a..6fce44e 100644 --- a/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath-NonInteractiveTerminal.txt +++ b/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath-NonInteractiveTerminal.txt @@ -8,6 +8,5 @@ Using regular unxip. Try passing `--experimental-unxip` for a faster unxip proce (5/6) Checking security assessment and code signing (6/6) Finishing installation xcodes requires superuser privileges in order to finish installation. -macOS User Password: Xcode 0.0.0 has been installed to /Applications/Xcode-0.0.0.app diff --git a/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath.txt b/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath.txt index 190aca5..861c015 100644 --- a/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath.txt +++ b/Tests/XcodesKitTests/Fixtures/LogOutput-FullHappyPath.txt @@ -110,6 +110,5 @@ Using regular unxip. Try passing `--experimental-unxip` for a faster unxip proce (5/6) Checking security assessment and code signing (6/6) Finishing installation xcodes requires superuser privileges in order to finish installation. -macOS User Password:  Xcode 0.0.0 has been installed to /Applications/Xcode-0.0.0.app diff --git a/Tests/XcodesKitTests/Fixtures/LogOutput-IncorrectSavedPassword.txt b/Tests/XcodesKitTests/Fixtures/LogOutput-IncorrectSavedPassword.txt index b3b59f4..d71ed41 100644 --- a/Tests/XcodesKitTests/Fixtures/LogOutput-IncorrectSavedPassword.txt +++ b/Tests/XcodesKitTests/Fixtures/LogOutput-IncorrectSavedPassword.txt @@ -112,6 +112,5 @@ Using regular unxip. Try passing `--experimental-unxip` for a faster unxip proce (5/6) Checking security assessment and code signing (6/6) Finishing installation xcodes requires superuser privileges in order to finish installation. -macOS User Password: Xcode 0.0.0 has been installed to /Applications/Xcode-0.0.0.app diff --git a/Tests/XcodesKitTests/XcodesKitTests.swift b/Tests/XcodesKitTests/XcodesKitTests.swift index 1282d92..817c1aa 100644 --- a/Tests/XcodesKitTests/XcodesKitTests.swift +++ b/Tests/XcodesKitTests/XcodesKitTests.swift @@ -184,18 +184,7 @@ final class XcodesKitTests: XCTestCase { Authority=\(XcodeInstaller.XcodeCertificateAuthority[2]) """)) } - // Don't have superuser privileges the first time - var validateSudoAuthenticationCallCount = 0 - XcodesKit.Current.shell.validateSudoAuthentication = { - validateSudoAuthenticationCallCount += 1 - - if validateSudoAuthenticationCallCount == 1 { - return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: nil)) - } - else { - return Promise.value(Shell.processOutputMock) - } - } + XcodesKit.Current.shell.authenticateSudoer = { Promise.value(Shell.processOutputMock) } // User enters password XcodesKit.Current.shell.readSecureLine = { prompt, _ in XcodesKit.Current.logging.log(prompt) @@ -277,18 +266,7 @@ final class XcodesKitTests: XCTestCase { Authority=\(XcodeInstaller.XcodeCertificateAuthority[2]) """)) } - // Don't have superuser privileges the first time - var validateSudoAuthenticationCallCount = 0 - XcodesKit.Current.shell.validateSudoAuthentication = { - validateSudoAuthenticationCallCount += 1 - - if validateSudoAuthenticationCallCount == 1 { - return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: nil)) - } - else { - return Promise.value(Shell.processOutputMock) - } - } + XcodesKit.Current.shell.authenticateSudoer = { Promise.value(Shell.processOutputMock) } // User enters password XcodesKit.Current.shell.readSecureLine = { prompt, _ in XcodesKit.Current.logging.log(prompt) @@ -374,18 +352,7 @@ final class XcodesKitTests: XCTestCase { Authority=\(XcodeInstaller.XcodeCertificateAuthority[2]) """)) } - // Don't have superuser privileges the first time - var validateSudoAuthenticationCallCount = 0 - XcodesKit.Current.shell.validateSudoAuthentication = { - validateSudoAuthenticationCallCount += 1 - - if validateSudoAuthenticationCallCount == 1 { - return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: nil)) - } - else { - return Promise.value(Shell.processOutputMock) - } - } + XcodesKit.Current.shell.authenticateSudoer = { Promise.value(Shell.processOutputMock) } // User enters password XcodesKit.Current.shell.readSecureLine = { prompt, _ in XcodesKit.Current.logging.log(prompt) @@ -467,18 +434,7 @@ final class XcodesKitTests: XCTestCase { Authority=\(XcodeInstaller.XcodeCertificateAuthority[2]) """)) } - // Don't have superuser privileges the first time - var validateSudoAuthenticationCallCount = 0 - XcodesKit.Current.shell.validateSudoAuthentication = { - validateSudoAuthenticationCallCount += 1 - - if validateSudoAuthenticationCallCount == 1 { - return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: nil)) - } - else { - return Promise.value(Shell.processOutputMock) - } - } + XcodesKit.Current.shell.authenticateSudoer = { Promise.value(Shell.processOutputMock) } // User enters password XcodesKit.Current.shell.readSecureLine = { prompt, _ in XcodesKit.Current.logging.log(prompt) @@ -579,18 +535,7 @@ final class XcodesKitTests: XCTestCase { Authority=\(XcodeInstaller.XcodeCertificateAuthority[2]) """)) } - // Don't have superuser privileges the first time - var validateSudoAuthenticationCallCount = 0 - XcodesKit.Current.shell.validateSudoAuthentication = { - validateSudoAuthenticationCallCount += 1 - - if validateSudoAuthenticationCallCount == 1 { - return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: nil)) - } - else { - return Promise.value(Shell.processOutputMock) - } - } + XcodesKit.Current.shell.authenticateSudoer = { Promise.value(Shell.processOutputMock) } // User enters password var readSecureLineCallCount = 0 XcodesKit.Current.shell.readSecureLine = { prompt, _ in @@ -613,7 +558,7 @@ final class XcodesKitTests: XCTestCase { expectation.fulfill() XCTAssertEqual(passwordEnvCallCount, 2) - XCTAssertEqual(readSecureLineCallCount, 2) + XCTAssertEqual(readSecureLineCallCount, 1) } .catch { XCTFail($0.localizedDescription) @@ -691,18 +636,7 @@ final class XcodesKitTests: XCTestCase { Authority=\(XcodeInstaller.XcodeCertificateAuthority[2]) """)) } - // Don't have superuser privileges the first time - var validateSudoAuthenticationCallCount = 0 - Current.shell.validateSudoAuthentication = { - validateSudoAuthenticationCallCount += 1 - - if validateSudoAuthenticationCallCount == 1 { - return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: nil)) - } - else { - return Promise.value(Shell.processOutputMock) - } - } + Current.shell.authenticateSudoer = { Promise.value(Shell.processOutputMock) } // User enters password Current.shell.readSecureLine = { prompt, _ in XcodesKit.Current.logging.log(prompt) @@ -779,7 +713,7 @@ final class XcodesKitTests: XCTestCase { } // Switching succeeds var selectedPaths: [String] = [] - Current.shell.xcodeSelectSwitch = { password, path in + Current.shell.xcodeSelectSwitch = { path in selectedPaths.append(path) return Promise.value((status: 0, out: "", err: "")) } @@ -991,25 +925,9 @@ final class XcodesKitTests: XCTestCase { return Promise.value((status: 0, out: "/Applications/Xcode-0.0.0.app/Contents/Developer", err: "")) } } - // Don't have superuser privileges the first time - var validateSudoAuthenticationCallCount = 0 - Current.shell.validateSudoAuthentication = { - validateSudoAuthenticationCallCount += 1 - - if validateSudoAuthenticationCallCount == 1 { - return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: nil)) - } - else { - return Promise.value(Shell.processOutputMock) - } - } - // User enters password - Current.shell.readSecureLine = { prompt, _ in - XcodesKit.Current.logging.log(prompt) - return "password" - } + Current.shell.authenticateSudoer = { Promise.value(Shell.processOutputMock) } // It successfully switches - Current.shell.xcodeSelectSwitch = { _, _ in + Current.shell.xcodeSelectSwitch = { _ in Promise.value((status: 0, out: "", err: "")) } @@ -1018,7 +936,6 @@ final class XcodesKitTests: XCTestCase { XCTAssertEqual(log, """ xcodes requires superuser privileges to select an Xcode - macOS User Password: Selected /Applications/Xcode-0.0.0.app/Contents/Developer """) @@ -1072,25 +989,9 @@ final class XcodesKitTests: XCTestCase { XcodesKit.Current.logging.log(prompt) return "1" } - // Don't have superuser privileges the first time - var validateSudoAuthenticationCallCount = 0 - Current.shell.validateSudoAuthentication = { - validateSudoAuthenticationCallCount += 1 - - if validateSudoAuthenticationCallCount == 1 { - return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: nil)) - } - else { - return Promise.value(Shell.processOutputMock) - } - } - // User enters password - Current.shell.readSecureLine = { prompt, _ in - XcodesKit.Current.logging.log(prompt) - return "password" - } + Current.shell.authenticateSudoer = { Promise.value(Shell.processOutputMock) } // It successfully switches - Current.shell.xcodeSelectSwitch = { _, _ in + Current.shell.xcodeSelectSwitch = { _ in Promise.value((status: 0, out: "", err: "")) } @@ -1103,7 +1004,6 @@ final class XcodesKitTests: XCTestCase { 2) 2.0.1 (ABC123) (Selected) Enter the number of the Xcode to select: xcodes requires superuser privileges to select an Xcode - macOS User Password: Selected /Applications/Xcode-0.0.0.app/Contents/Developer """) @@ -1157,7 +1057,7 @@ final class XcodesKitTests: XCTestCase { } } // It successfully switches - Current.shell.xcodeSelectSwitch = { _, _ in + Current.shell.xcodeSelectSwitch = { _ in Promise.value((status: 0, out: "", err: "")) } @@ -1165,6 +1065,7 @@ final class XcodesKitTests: XCTestCase { .cauterize() XCTAssertEqual(log, """ + xcodes requires superuser privileges to select an Xcode Selected /Applications/Xcode-2.0.1.app/Contents/Developer """)