diff --git a/Sources/XcodesKit/Environment.swift b/Sources/XcodesKit/Environment.swift index fec957d..60c5acb 100644 --- a/Sources/XcodesKit/Environment.swift +++ b/Sources/XcodesKit/Environment.swift @@ -23,7 +23,7 @@ public struct Environment { public var Current = Environment() public struct Shell { - public var unxip: (URL) -> Promise = { Process.run(Path.root.usr.bin.xip, workingDirectory: $0.deletingLastPathComponent(), "--expand", "\($0.path)") } + public var unxip: (URL, URL) -> Promise = { Process.run(Path.root.usr.bin.xip, workingDirectory: $1, "--expand", "\($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") } @@ -238,6 +238,12 @@ public struct Files { try createDirectory(url, createIntermediates, attributes) } + public var temporalDirectory: (URL) throws -> URL = { try FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: $0, create: true) } + + public func temporalDirectory(for URL: URL) throws -> URL { + return try temporalDirectory(URL) + } + public var installedXcodes = XcodesKit.installedXcodes } private func installedXcodes(directory: Path) -> [InstalledXcode] { diff --git a/Sources/XcodesKit/XcodeInstaller.swift b/Sources/XcodesKit/XcodeInstaller.swift index 30fecca..4aa05df 100644 --- a/Sources/XcodesKit/XcodeInstaller.swift +++ b/Sources/XcodesKit/XcodeInstaller.swift @@ -708,9 +708,10 @@ public final class XcodeInstaller { } func unarchiveAndMoveXIP(at source: URL, to destination: URL) -> Promise { + let xcodeExpansionDirectory = (try? Current.files.temporalDirectory(for: source)) ?? source.deletingLastPathComponent() return firstly { () -> Promise in Current.logging.log(InstallationStep.unarchiving.description) - return Current.shell.unxip(source) + return Current.shell.unxip(source, xcodeExpansionDirectory) .recover { (error) throws -> Promise in if case Process.PMKError.execution(_, _, let standardError) = error, standardError?.contains("damaged and can’t be expanded") == true { @@ -722,8 +723,8 @@ public final class XcodeInstaller { .map { output -> URL in Current.logging.log(InstallationStep.moving(destination: destination.path).description) - let xcodeURL = source.deletingLastPathComponent().appendingPathComponent("Xcode.app") - let xcodeBetaURL = source.deletingLastPathComponent().appendingPathComponent("Xcode-beta.app") + let xcodeURL = xcodeExpansionDirectory.appendingPathComponent("Xcode.app") + let xcodeBetaURL = xcodeExpansionDirectory.appendingPathComponent("Xcode-beta.app") if Current.files.fileExists(atPath: xcodeURL.path) { try Current.files.moveItem(at: xcodeURL, to: destination) } diff --git a/Tests/XcodesKitTests/Environment+Mock.swift b/Tests/XcodesKitTests/Environment+Mock.swift index 860cce1..cfd358e 100644 --- a/Tests/XcodesKitTests/Environment+Mock.swift +++ b/Tests/XcodesKitTests/Environment+Mock.swift @@ -16,7 +16,7 @@ extension Shell { static var processOutputMock: ProcessOutput = (0, "", "") static var mock = Shell( - unxip: { _ in return Promise.value(Shell.processOutputMock) }, + unxip: { _, _ 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) }, @@ -60,6 +60,7 @@ extension Files { trashItem: { _ in return URL(fileURLWithPath: "\(NSHomeDirectory())/.Trash") }, createFile: { _, _, _ in return true }, createDirectory: { _, _, _ in }, + temporalDirectory: { _ in return URL(fileURLWithPath: NSTemporaryDirectory()) }, installedXcodes: { _ in [] } ) } diff --git a/Tests/XcodesKitTests/XcodesKitTests.swift b/Tests/XcodesKitTests/XcodesKitTests.swift index 947716e..0aafb73 100644 --- a/Tests/XcodesKitTests/XcodesKitTests.swift +++ b/Tests/XcodesKitTests/XcodesKitTests.swift @@ -707,7 +707,7 @@ final class XcodesKitTests: XCTestCase { XcodesKit.Current.logging.log(prompt) return "asdf" } - Current.shell.unxip = { _ in + Current.shell.unxip = { _, _ in unxipCallCount += 1 if unxipCallCount == 1 { return Promise(error: Process.PMKError.execution(process: Process(), standardOutput: nil, standardError: "The file \"Xcode-0.0.0.xip\" is damaged and can’t be expanded."))