From 95ca2bc1c3332c1a3543b75920ea05e7b6bbdad5 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Thu, 31 Dec 2020 16:04:32 -0700 Subject: [PATCH 1/5] Remove PromiseKit and PMKFoundation --- Xcodes.xcodeproj/project.pbxproj | 36 +------------ .../xcshareddata/swiftpm/Package.resolved | 18 ------- Xcodes/Backend/Environment.swift | 2 - Xcodes/Backend/Process.swift | 12 ++++- Xcodes/Frontend/XcodeList/XcodeListView.swift | 1 - Xcodes/Resources/Licenses.rtf | 54 +------------------ 6 files changed, 13 insertions(+), 110 deletions(-) diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index f0a8f89..2a053a2 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -55,9 +55,7 @@ CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B42592EEEA00380FEE /* Process.swift */; }; CABFA9DF2592F07A00380FEE /* Path in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9DE2592F07A00380FEE /* Path */; }; CABFA9E42592F08E00380FEE /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9E32592F08E00380FEE /* Version */; }; - CABFA9E92592F0B400380FEE /* PromiseKit in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9E82592F0B400380FEE /* PromiseKit */; }; CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9ED2592F0CC00380FEE /* SwiftSoup */; }; - CABFA9F32592F0E400380FEE /* PMKFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9F22592F0E400380FEE /* PMKFoundation */; }; CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9F72592F0F900380FEE /* KeychainAccess */; }; CABFA9FD2592F13300380FEE /* LegibleError in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9FC2592F13300380FEE /* LegibleError */; }; CABFAA2C2592FBFC00380FEE /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFAA2A2592FBFC00380FEE /* SettingsView.swift */; }; @@ -218,11 +216,9 @@ buildActionMask = 2147483647; files = ( CABFA9E42592F08E00380FEE /* Version in Frameworks */, - CABFA9E92592F0B400380FEE /* PromiseKit in Frameworks */, CABFA9FD2592F13300380FEE /* LegibleError in Frameworks */, CA9FF86D25951C6E00E47BAF /* XCModel in Frameworks */, CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */, - CABFA9F32592F0E400380FEE /* PMKFoundation in Frameworks */, CAA1CB2D255A5262003FD669 /* AppleAPI in Frameworks */, CABFA9DF2592F07A00380FEE /* Path in Frameworks */, CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */, @@ -479,9 +475,7 @@ CAA1CB2C255A5262003FD669 /* AppleAPI */, CABFA9DE2592F07A00380FEE /* Path */, CABFA9E32592F08E00380FEE /* Version */, - CABFA9E82592F0B400380FEE /* PromiseKit */, CABFA9ED2592F0CC00380FEE /* SwiftSoup */, - CABFA9F22592F0E400380FEE /* PMKFoundation */, CABFA9F72592F0F900380FEE /* KeychainAccess */, CABFA9FC2592F13300380FEE /* LegibleError */, CA9FF86C25951C6E00E47BAF /* XCModel */, @@ -542,9 +536,7 @@ packageReferences = ( CABFA9DD2592F07A00380FEE /* XCRemoteSwiftPackageReference "Path" */, CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */, - CABFA9E72592F0B400380FEE /* XCRemoteSwiftPackageReference "PromiseKit" */, CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */, - CABFA9F12592F0E400380FEE /* XCRemoteSwiftPackageReference "Foundation" */, CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */, CABFA9FB2592F13300380FEE /* XCRemoteSwiftPackageReference "LegibleError" */, CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */, @@ -589,6 +581,8 @@ inputFileListPaths = ( ); inputPaths = ( + "$(SRCROOT)/Xcodes.xcodeproj", + "$(SRCROOT)/**/*.LICENSE", ); name = "Generate Acknowledgements"; outputFileListPaths = ( @@ -1145,14 +1139,6 @@ minimumVersion = 1.0.3; }; }; - CABFA9E72592F0B400380FEE /* XCRemoteSwiftPackageReference "PromiseKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/mxcl/PromiseKit"; - requirement = { - kind = upToNextMinorVersion; - minimumVersion = 6.8.3; - }; - }; CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/scinfu/SwiftSoup"; @@ -1161,14 +1147,6 @@ minimumVersion = 2.0.0; }; }; - CABFA9F12592F0E400380FEE /* XCRemoteSwiftPackageReference "Foundation" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/PromiseKit/Foundation"; - requirement = { - kind = upToNextMinorVersion; - minimumVersion = 3.3.1; - }; - }; CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess"; @@ -1207,21 +1185,11 @@ package = CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */; productName = Version; }; - CABFA9E82592F0B400380FEE /* PromiseKit */ = { - isa = XCSwiftPackageProductDependency; - package = CABFA9E72592F0B400380FEE /* XCRemoteSwiftPackageReference "PromiseKit" */; - productName = PromiseKit; - }; CABFA9ED2592F0CC00380FEE /* SwiftSoup */ = { isa = XCSwiftPackageProductDependency; package = CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; - CABFA9F22592F0E400380FEE /* PMKFoundation */ = { - isa = XCSwiftPackageProductDependency; - package = CABFA9F12592F0E400380FEE /* XCRemoteSwiftPackageReference "Foundation" */; - productName = PMKFoundation; - }; CABFA9F72592F0F900380FEE /* KeychainAccess */ = { isa = XCSwiftPackageProductDependency; package = CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */; diff --git a/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index fba9785..880f3a4 100644 --- a/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -10,15 +10,6 @@ "version": null } }, - { - "package": "PMKFoundation", - "repositoryURL": "https://github.com/PromiseKit/Foundation", - "state": { - "branch": null, - "revision": "1a276e598dac59489ed904887e0740fa75e571e0", - "version": "3.3.4" - } - }, { "package": "KeychainAccess", "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess", @@ -46,15 +37,6 @@ "version": "0.16.3" } }, - { - "package": "PromiseKit", - "repositoryURL": "https://github.com/mxcl/PromiseKit", - "state": { - "branch": null, - "revision": "1c296a8637838901d2b01e4c46875ee749506133", - "version": "6.8.5" - } - }, { "package": "SwiftSoup", "repositoryURL": "https://github.com/scinfu/SwiftSoup", diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index 99f84be..26555ad 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -1,7 +1,5 @@ import Combine import Foundation -import PromiseKit -import PMKFoundation import Path import AppleAPI import KeychainAccess diff --git a/Xcodes/Backend/Process.swift b/Xcodes/Backend/Process.swift index 779f151..b800d53 100644 --- a/Xcodes/Backend/Process.swift +++ b/Xcodes/Backend/Process.swift @@ -1,7 +1,5 @@ import Combine import Foundation -import PromiseKit -import PMKFoundation import Path public typealias ProcessOutput = (status: Int32, out: String, err: String) @@ -71,6 +69,10 @@ extension Process { let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" + + guard process.terminationReason == .exit, process.terminationStatus == 0 else { + return promise(.failure(ProcessExecutionError(process: process, standardOutput: output, standardError: error))) + } promise(.success((process.terminationStatus, output, error))) } catch { @@ -83,3 +85,9 @@ extension Process { .eraseToAnyPublisher() } } + +struct ProcessExecutionError: Error { + let process: Process + let standardOutput: String + let standardError: String +} diff --git a/Xcodes/Frontend/XcodeList/XcodeListView.swift b/Xcodes/Frontend/XcodeList/XcodeListView.swift index c3c564f..6293083 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListView.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListView.swift @@ -1,6 +1,5 @@ import SwiftUI import Version -import PromiseKit struct XcodeListView: View { @EnvironmentObject var appState: AppState diff --git a/Xcodes/Resources/Licenses.rtf b/Xcodes/Resources/Licenses.rtf index 569081d..3dc4006 100644 --- a/Xcodes/Resources/Licenses.rtf +++ b/Xcodes/Resources/Licenses.rtf @@ -4,33 +4,7 @@ {\*\expandedcolortbl;;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 -\f0\fs34 \cf0 PromiseKit\ -\ - -\fs26 Copyright 2016-present, Max Howell; mxcl@me.com\ -\ -Permission is hereby granted, free of charge, to any person obtaining a\ -copy of this software and associated documentation files (the\ -"Software"), to deal in the Software without restriction, including\ -without limitation the rights to use, copy, modify, merge, publish,\ -distribute, sublicense, and/or sell copies of the Software, and to\ -permit persons to whom the Software is furnished to do so, subject to\ -the following conditions:\ -\ -The above copyright notice and this permission notice shall be included\ -in all copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\ -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\ -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\ -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\ -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\ -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\ -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\ -\ -\ - -\fs34 SwiftSoup\ +\f0\fs34 \cf0 SwiftSoup\ \ \fs26 MIT License\ @@ -57,32 +31,6 @@ SOFTWARE.\ \ \ -\fs34 Foundation\ -\ - -\fs26 Copyright 2018-present, Max Howell; mxcl@me.com\ -\ -Permission is hereby granted, free of charge, to any person obtaining a\ -copy of this software and associated documentation files (the\ -"Software"), to deal in the Software without restriction, including\ -without limitation the rights to use, copy, modify, merge, publish,\ -distribute, sublicense, and/or sell copies of the Software, and to\ -permit persons to whom the Software is furnished to do so, subject to\ -the following conditions:\ -\ -The above copyright notice and this permission notice shall be included\ -in all copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\ -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\ -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\ -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\ -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\ -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\ -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\ -\ -\ - \fs34 Path.swift\ \ From fa277ccaae2799194c0bb214de07cf452aa39b79 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Fri, 1 Jan 2021 10:37:33 -0700 Subject: [PATCH 2/5] Convert URLSession.downloadTask to Combine --- Xcodes.xcodeproj/project.pbxproj | 8 ++--- Xcodes/Backend/Environment.swift | 8 ++--- ...=> URLSession+DownloadTaskPublisher.swift} | 30 +++++++++++-------- 3 files changed, 26 insertions(+), 20 deletions(-) rename Xcodes/Backend/{URLSession+Promise.swift => URLSession+DownloadTaskPublisher.swift} (60%) diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 2a053a2..7a04c70 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -41,7 +41,7 @@ CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB4C255A5CFD003FD669 /* SignInPhoneListView.swift */; }; CABFA9BB2592EEEA00380FEE /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9BA2592EEEA00380FEE /* DateFormatter+.swift */; }; CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A92592EEE900380FEE /* Environment.swift */; }; - CABFA9BF2592EEEA00380FEE /* URLSession+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B32592EEEA00380FEE /* URLSession+Promise.swift */; }; + CABFA9BF2592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */; }; CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A82592EEE900380FEE /* Version+.swift */; }; CABFA9C22592EEEA00380FEE /* Promise+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B02592EEEA00380FEE /* Promise+.swift */; }; CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B92592EEEA00380FEE /* Downloads.swift */; }; @@ -171,7 +171,7 @@ CABFA9AE2592EEE900380FEE /* Path+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Path+.swift"; sourceTree = ""; }; CABFA9B02592EEEA00380FEE /* Promise+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+.swift"; sourceTree = ""; }; CABFA9B22592EEEA00380FEE /* Entry+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Entry+.swift"; sourceTree = ""; }; - CABFA9B32592EEEA00380FEE /* URLSession+Promise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+Promise.swift"; sourceTree = ""; }; + CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+DownloadTaskPublisher.swift"; sourceTree = ""; }; CABFA9B42592EEEA00380FEE /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = ""; }; CABFA9B82592EEEA00380FEE /* FileManager+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+.swift"; sourceTree = ""; }; CABFA9B92592EEEA00380FEE /* Downloads.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloads.swift; sourceTree = ""; }; @@ -338,7 +338,7 @@ CABFA9B02592EEEA00380FEE /* Promise+.swift */, CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */, CABFA9AB2592EEE900380FEE /* URLRequest+Apple.swift */, - CABFA9B32592EEEA00380FEE /* URLSession+Promise.swift */, + CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */, CABFA9A82592EEE900380FEE /* Version+.swift */, CA9FF876259528CC00E47BAF /* Version+XcodeReleases.swift */, CABFA9A62592EEE900380FEE /* Version+Xcode.swift */, @@ -623,7 +623,7 @@ CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */, CABFA9CA2592EEEA00380FEE /* AppState+Update.swift in Sources */, CA44901F2463AD34003D8213 /* Tag.swift in Sources */, - CABFA9BF2592EEEA00380FEE /* URLSession+Promise.swift in Sources */, + CABFA9BF2592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift in Sources */, CABFA9BB2592EEEA00380FEE /* DateFormatter+.swift in Sources */, CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */, CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */, diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index 26555ad..48b4f96 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -113,10 +113,10 @@ public struct Network { dataTask(request) } - public var downloadTask: (URLRequestConvertible, URL, Data?) -> (Progress, Promise<(saveLocation: URL, response: URLResponse)>) = { AppleAPI.Current.network.session.downloadTask(with: $0, to: $1, resumingWith: $2) } - - public func downloadTask(with convertible: URLRequestConvertible, to saveLocation: URL, resumingWith resumeData: Data?) -> (progress: Progress, promise: Promise<(saveLocation: URL, response: URLResponse)>) { - return downloadTask(convertible, saveLocation, resumeData) + public var downloadTask: (URL, URL, Data?) -> (Progress, AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) = { AppleAPI.Current.network.session.downloadTask(with: $0, to: $1, resumingWith: $2) } + + public func downloadTask(with url: URL, to saveLocation: URL, resumingWith resumeData: Data?) -> (progress: Progress, publisher: AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) { + return downloadTask(url, saveLocation, resumeData) } } diff --git a/Xcodes/Backend/URLSession+Promise.swift b/Xcodes/Backend/URLSession+DownloadTaskPublisher.swift similarity index 60% rename from Xcodes/Backend/URLSession+Promise.swift rename to Xcodes/Backend/URLSession+DownloadTaskPublisher.swift index 04ee10e..d721712 100644 --- a/Xcodes/Backend/URLSession+Promise.swift +++ b/Xcodes/Backend/URLSession+DownloadTaskPublisher.swift @@ -1,6 +1,5 @@ +import Combine import Foundation -import PromiseKit -import PMKFoundation extension URLSession { /** @@ -8,40 +7,47 @@ extension URLSession { - Parameter saveLocation: A URL to move the downloaded file to after it completes. Apple deletes the temporary file immediately after the underyling completion handler returns. - Parameter resumeData: Data describing the state of a previously cancelled or failed download task. See the Discussion section for `downloadTask(withResumeData:completionHandler:)` https://developer.apple.com/documentation/foundation/urlsession/1411598-downloadtask# - - Returns: Tuple containing a Progress object for the task and a promise containing the save location and response. + - Returns: Tuple containing a Progress object for the task and a publisher of the save location and response. - Note: We do not create the destination directory for you, because we move the file with FileManager.moveItem which changes its behavior depending on the directory status of the URL you provide. So create your own directory first! */ - public func downloadTask(with convertible: URLRequestConvertible, to saveLocation: URL, resumingWith resumeData: Data?) -> (progress: Progress, promise: Promise<(saveLocation: URL, response: URLResponse)>) { + public func downloadTask( + with url: URL, + to saveLocation: URL, + resumingWith resumeData: Data? + ) -> (progress: Progress, publisher: AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) { var progress: Progress! - let promise = Promise<(saveLocation: URL, response: URLResponse)> { seal in + // Intentionally not wrapping in Deferred because we need to return the Progress! immediately. + // Probably a sign that this should be implemented differently... + let promise = Future<(saveLocation: URL, response: URLResponse), Error> { promise in let completionHandler = { (temporaryURL: URL?, response: URLResponse?, error: Error?) in if let error = error { - seal.reject(error) + promise(.failure(error)) } else if let response = response, let temporaryURL = temporaryURL { do { try FileManager.default.moveItem(at: temporaryURL, to: saveLocation) - seal.fulfill((saveLocation, response)) + promise(.success((saveLocation, response))) } catch { - seal.reject(error) + promise(.failure(error)) } } else { - seal.reject(PMKError.invalidCallingConvention) + fatalError("Expecting either a temporary URL and a response, or an error, but got neither.") } } let task: URLSessionDownloadTask if let resumeData = resumeData { - task = downloadTask(withResumeData: resumeData, completionHandler: completionHandler) + task = self.downloadTask(withResumeData: resumeData, completionHandler: completionHandler) } else { - task = downloadTask(with: convertible.pmkRequest, completionHandler: completionHandler) + task = self.downloadTask(with: url, completionHandler: completionHandler) } progress = task.progress task.resume() } + .eraseToAnyPublisher() return (progress, promise) } -} \ No newline at end of file +} From 9971e0b45d23587579b2c6efb0be52dfc207da31 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Fri, 1 Jan 2021 10:37:47 -0700 Subject: [PATCH 3/5] Remove Promise retry functions We'll need to reimplement these later when implementing installation --- Xcodes.xcodeproj/project.pbxproj | 4 ---- Xcodes/Backend/Promise+.swift | 40 -------------------------------- 2 files changed, 44 deletions(-) delete mode 100644 Xcodes/Backend/Promise+.swift diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 7a04c70..ed443f8 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -43,7 +43,6 @@ CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A92592EEE900380FEE /* Environment.swift */; }; CABFA9BF2592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */; }; CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A82592EEE900380FEE /* Version+.swift */; }; - CABFA9C22592EEEA00380FEE /* Promise+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B02592EEEA00380FEE /* Promise+.swift */; }; CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B92592EEEA00380FEE /* Downloads.swift */; }; CABFA9C52592EEEA00380FEE /* FileManager+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B82592EEEA00380FEE /* FileManager+.swift */; }; CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B22592EEEA00380FEE /* Entry+.swift */; }; @@ -169,7 +168,6 @@ CABFA9AB2592EEE900380FEE /* URLRequest+Apple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Apple.swift"; sourceTree = ""; }; CABFA9AC2592EEE900380FEE /* Foundation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Foundation.swift; sourceTree = ""; }; CABFA9AE2592EEE900380FEE /* Path+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Path+.swift"; sourceTree = ""; }; - CABFA9B02592EEEA00380FEE /* Promise+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+.swift"; sourceTree = ""; }; CABFA9B22592EEEA00380FEE /* Entry+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Entry+.swift"; sourceTree = ""; }; CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+DownloadTaskPublisher.swift"; sourceTree = ""; }; CABFA9B42592EEEA00380FEE /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = ""; }; @@ -335,7 +333,6 @@ CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */, CABFA9AE2592EEE900380FEE /* Path+.swift */, CABFA9B42592EEEA00380FEE /* Process.swift */, - CABFA9B02592EEEA00380FEE /* Promise+.swift */, CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */, CABFA9AB2592EEE900380FEE /* URLRequest+Apple.swift */, CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */, @@ -647,7 +644,6 @@ CA9FF8F525959CE000E47BAF /* HelperInstaller.swift in Sources */, CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */, CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */, - CABFA9C22592EEEA00380FEE /* Promise+.swift in Sources */, CAFBDC68259A308B003DCC5A /* InfoPane.swift in Sources */, CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */, CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */, diff --git a/Xcodes/Backend/Promise+.swift b/Xcodes/Backend/Promise+.swift deleted file mode 100644 index 9398cd7..0000000 --- a/Xcodes/Backend/Promise+.swift +++ /dev/null @@ -1,40 +0,0 @@ -import Foundation -import PromiseKit - -/// Attempt and retry a task that fails with resume data up to `maximumRetryCount` times -func attemptResumableTask( - maximumRetryCount: Int = 3, - delayBeforeRetry: DispatchTimeInterval = .seconds(2), - _ body: @escaping (Data?) -> Promise -) -> Promise { - var attempts = 0 - func attempt(with resumeData: Data? = nil) -> Promise { - attempts += 1 - return body(resumeData).recover { error -> Promise in - guard - attempts < maximumRetryCount, - let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data - else { throw error } - - return after(delayBeforeRetry).then(on: nil) { attempt(with: resumeData) } - } - } - return attempt() -} - -/// Attempt and retry a task up to `maximumRetryCount` times -func attemptRetryableTask( - maximumRetryCount: Int = 3, - delayBeforeRetry: DispatchTimeInterval = .seconds(2), - _ body: @escaping () -> Promise -) -> Promise { - var attempts = 0 - func attempt() -> Promise { - attempts += 1 - return body().recover { error -> Promise in - guard attempts < maximumRetryCount else { throw error } - return after(delayBeforeRetry).then(on: nil) { attempt() } - } - } - return attempt() -} From f00ae396a612ba142ab7f6e3f36a03b2deee553d Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Fri, 1 Jan 2021 10:40:41 -0700 Subject: [PATCH 4/5] Remove Current.shell sudo functionality This'll be done in the helper --- Xcodes/Backend/Environment.swift | 17 ----------------- Xcodes/Backend/Process.swift | 9 --------- 2 files changed, 26 deletions(-) diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index 48b4f96..19644a1 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -27,28 +27,11 @@ public struct Shell { public var unxip: (URL) -> Promise = { Process.run(Path.root.usr.bin.xip, workingDirectory: $0.deletingLastPathComponent(), "--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") } - 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 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 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 xcodeSelectPrintPath: () -> AnyPublisher = { Process.run(Path.root.usr.bin.join("xcode-select"), "-p") } } diff --git a/Xcodes/Backend/Process.swift b/Xcodes/Backend/Process.swift index b800d53..d5559b9 100644 --- a/Xcodes/Backend/Process.swift +++ b/Xcodes/Backend/Process.swift @@ -5,15 +5,6 @@ import Path 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) - } - @discardableResult static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) -> Promise { return run(executable.url, workingDirectory: workingDirectory, input: input, arguments) From 245780023bca9573efdc131ef1d5a5f69f63a6cd Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Fri, 1 Jan 2021 10:41:08 -0700 Subject: [PATCH 5/5] Convert Current.shell to Combine --- Xcodes/Backend/Environment.swift | 14 +++++++------- Xcodes/Backend/Process.swift | 26 +------------------------- 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index 19644a1..203c5f4 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -24,13 +24,13 @@ 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 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 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 unxip: (URL) -> AnyPublisher = { Process.run(Path.root.usr.bin.xip, workingDirectory: $0.deletingLastPathComponent(), "--expand", "\($0.path)") } + public var spctlAssess: (URL) -> AnyPublisher = { Process.run(Path.root.usr.sbin.spctl, "--assess", "--verbose", "--type", "execute", "\($0.path)") } + public var codesignVerify: (URL) -> AnyPublisher = { Process.run(Path.root.usr.bin.codesign, "-vv", "-d", "\($0.path)") } + public var buildVersion: () -> AnyPublisher = { Process.run(Path.root.usr.bin.sw_vers, "-buildVersion") } + public var xcodeBuildVersion: (InstalledXcode) -> AnyPublisher = { Process.run(Path.root.usr.libexec.PlistBuddy, "-c", "Print :ProductBuildVersion", "\($0.path.string)/Contents/version.plist") } + public var getUserCacheDir: () -> AnyPublisher = { Process.run(Path.root.usr.bin.getconf, "DARWIN_USER_CACHE_DIR") } + public var touchInstallCheck: (String, String, String) -> AnyPublisher = { Process.run(Path.root.usr.bin/"touch", "\($0)com.apple.dt.Xcode.InstallCheckCache_\($1)_\($2)") } public var xcodeSelectPrintPath: () -> AnyPublisher = { Process.run(Path.root.usr.bin.join("xcode-select"), "-p") } } diff --git a/Xcodes/Backend/Process.swift b/Xcodes/Backend/Process.swift index d5559b9..f09bd04 100644 --- a/Xcodes/Backend/Process.swift +++ b/Xcodes/Backend/Process.swift @@ -4,31 +4,7 @@ import Path public typealias ProcessOutput = (status: Int32, out: String, err: String) -extension Process { - @discardableResult - static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) -> Promise { - return run(executable.url, workingDirectory: workingDirectory, input: input, arguments) - } - - @discardableResult - static func run(_ executable: URL, workingDirectory: URL? = nil, input: String? = nil, _ arguments: [String]) -> Promise { - let process = Process() - process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent() - process.executableURL = executable - process.arguments = arguments - if let input = input { - let inputPipe = Pipe() - process.standardInput = inputPipe.fileHandleForReading - inputPipe.fileHandleForWriting.write(Data(input.utf8)) - inputPipe.fileHandleForWriting.closeFile() - } - return process.launch(.promise).map { std in - let output = String(data: std.out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" - let error = String(data: std.err.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" - return (process.terminationStatus, output, error) - } - } - +extension Process { @discardableResult static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) -> AnyPublisher { return run(executable.url, workingDirectory: workingDirectory, input: input, arguments)