diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index c7b7c7d..3186a93 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ CA25192A25A9644800F08414 /* XcodeInstallState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA25192925A9644800F08414 /* XcodeInstallState.swift */; }; CA378F992466567600A58CE0 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA378F982466567600A58CE0 /* AppState.swift */; }; CA39711924495F0E00AFFB77 /* AppStoreButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */; }; + CA42DD6E25AEA8B200BC0B0C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA42DD6D25AEA8B200BC0B0C /* Logger.swift */; }; + CA42DD7325AEB04300BC0B0C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA42DD7225AEB04300BC0B0C /* Logger.swift */; }; CA44901F2463AD34003D8213 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA44901E2463AD34003D8213 /* Tag.swift */; }; CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */; }; CA452BC1259FDDFE0072DFA4 /* Stub-version.plist in Resources */ = {isa = PBXBuildFile; fileRef = CA452BBF259FDDFE0072DFA4 /* Stub-version.plist */; }; @@ -153,6 +155,8 @@ CA25192925A9644800F08414 /* XcodeInstallState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeInstallState.swift; sourceTree = ""; }; CA378F982466567600A58CE0 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStoreButtonStyle.swift; sourceTree = ""; }; + CA42DD6D25AEA8B200BC0B0C /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; + CA42DD7225AEB04300BC0B0C /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; CA44901E2463AD34003D8213 /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = ""; }; CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; }; CA452BBF259FDDFE0072DFA4 /* Stub-version.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Stub-version.plist"; sourceTree = ""; }; @@ -337,6 +341,7 @@ CA9FF8E525959BB800E47BAF /* AuditTokenHack.m */, CA9FF8EA25959BDD00E47BAF /* com.robotsandpencils.XcodesApp.Helper-Bridging-Header.h */, CA9FF8DF25959BAA00E47BAF /* ConnectionVerifier.swift */, + CA42DD7225AEB04300BC0B0C /* Logger.swift */, CA9FF8B02595967A00E47BAF /* main.swift */, CA9FF8DA25959B4000E47BAF /* XPCDelegate.swift */, CA9FF8C22595988B00E47BAF /* Info.plist */, @@ -409,6 +414,7 @@ CAC281D9259F985100B8AB0B /* InstallationStep.swift */, CA9FF8862595607900E47BAF /* InstalledXcode.swift */, CAA8587B25A2B37900ACF8C0 /* IsTesting.swift */, + CA42DD6D25AEA8B200BC0B0C /* Logger.swift */, CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */, CABFA9AE2592EEE900380FEE /* Path+.swift */, CABFA9B42592EEEA00380FEE /* Process.swift */, @@ -698,6 +704,7 @@ buildActionMask = 2147483647; files = ( CA9FF8D025959A9700E47BAF /* HelperXPCShared.swift in Sources */, + CA42DD7325AEB04300BC0B0C /* Logger.swift in Sources */, CA9FF8DB25959B4000E47BAF /* XPCDelegate.swift in Sources */, CA9FF8E625959BB800E47BAF /* AuditTokenHack.m in Sources */, CA9FF8B12595967A00E47BAF /* main.swift in Sources */, @@ -732,6 +739,7 @@ CABFA9C52592EEEA00380FEE /* FileManager+.swift in Sources */, CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */, CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */, + CA42DD6E25AEA8B200BC0B0C /* Logger.swift in Sources */, CA61A6E0259835580008926E /* Xcode.swift in Sources */, CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */, CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */, diff --git a/Xcodes/AppleAPI/Sources/AppleAPI/Environment.swift b/Xcodes/AppleAPI/Sources/AppleAPI/Environment.swift index 5c76749..53c5fc5 100644 --- a/Xcodes/AppleAPI/Sources/AppleAPI/Environment.swift +++ b/Xcodes/AppleAPI/Sources/AppleAPI/Environment.swift @@ -10,7 +10,6 @@ import Combine */ public struct Environment { public var network = Network() - public var logging = Logging() } public var Current = Environment() @@ -23,7 +22,3 @@ public struct Network { dataTask(request) } } - -public struct Logging { - public var log: (String) -> Void = { print($0) } -} diff --git a/Xcodes/Backend/AppState+Install.swift b/Xcodes/Backend/AppState+Install.swift index dc250e3..4a47a20 100644 --- a/Xcodes/Backend/AppState+Install.swift +++ b/Xcodes/Backend/AppState+Install.swift @@ -4,6 +4,7 @@ import Path import AppleAPI import Version import LegibleError +import os.log /// Downloads and installs Xcodes extension AppState { @@ -27,8 +28,8 @@ extension AppState { case .version: // If the XIP was just downloaded, remove it and try to recover. do { - Current.logging.log(error.legibleLocalizedDescription) - Current.logging.log("Removing damaged XIP and re-attempting installation.\n") + Logger.appState.error("\(error.legibleLocalizedDescription)") + Logger.appState.info("Removing damaged XIP and re-attempting installation.") try Current.files.removeItem(at: damagedXIPURL) return self.install(installationType, downloader: downloader, attemptNumber: attemptNumber + 1) .eraseToAnyPublisher() @@ -82,7 +83,7 @@ extension AppState { aria2DownloadIsIncomplete = true } if Current.files.fileExistsAtPath(expectedArchivePath.string), aria2DownloadIsIncomplete == false { - Current.logging.log("(1/6) Found existing archive that will be used for installation at \(expectedArchivePath).") + Logger.appState.info("Found existing archive that will be used for installation at \(expectedArchivePath).") return Just(expectedArchivePath.url) .setFailureType(to: Error.self) .eraseToAnyPublisher() diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index d403faa..b812835 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -15,7 +15,6 @@ public struct Environment { public var shell = Shell() public var files = Files() public var network = Network() - public var logging = Logging() public var keychain = Keychain() public var defaults = Defaults() public var date: () -> Date = Date.init @@ -190,10 +189,6 @@ public struct Network { } } -public struct Logging { - public var log: (String) -> Void = { print($0) } -} - public struct Keychain { private static let keychain = KeychainAccess.Keychain(service: "com.robotsandpencils.XcodesApp") diff --git a/Xcodes/Backend/HelperInstaller.swift b/Xcodes/Backend/HelperInstaller.swift index eb997d9..af70188 100644 --- a/Xcodes/Backend/HelperInstaller.swift +++ b/Xcodes/Backend/HelperInstaller.swift @@ -1,6 +1,7 @@ // From https://github.com/securing/SimpleXPCApp/ import Foundation +import os.log import ServiceManagement enum HelperAuthorizationError: Error { @@ -35,8 +36,9 @@ class HelperInstaller { let authRef = try authorizationRef(&authRights, nil, [.interactionAllowed, .extendRights, .preAuthorize]) var cfError: Unmanaged? SMJobBless(kSMDomainSystemLaunchd, machServiceName as CFString, authRef, &cfError) - } catch let err { - print("Error in installing the helper -> \(err.localizedDescription)") + if let error = cfError?.takeRetainedValue() { throw error } + } catch { + Logger.helperInstaller.error("\(error.localizedDescription)") } } } diff --git a/Xcodes/Backend/Logger.swift b/Xcodes/Backend/Logger.swift new file mode 100644 index 0000000..d83f9b3 --- /dev/null +++ b/Xcodes/Backend/Logger.swift @@ -0,0 +1,10 @@ +import Foundation +import os.log + +extension Logger { + private static var subsystem = Bundle.main.bundleIdentifier! + + static let appState = Logger(subsystem: subsystem, category: "appState") + static let helperInstaller = Logger(subsystem: subsystem, category: "helperInstaller") + static let subprocess = Logger(subsystem: subsystem, category: "subprocess") +} diff --git a/Xcodes/Backend/Process.swift b/Xcodes/Backend/Process.swift index 88cdf8c..fe96001 100644 --- a/Xcodes/Backend/Process.swift +++ b/Xcodes/Backend/Process.swift @@ -1,5 +1,6 @@ import Combine import Foundation +import os.log import Path public typealias ProcessOutput = (status: Int32, out: String, err: String) @@ -32,16 +33,19 @@ extension Process { } do { - print("Process.run \(executable), \(input), \(arguments.joined(separator: " "))") + Logger.subprocess.info("Process.run executable: \(executable), input: \(input ?? ""), arguments: \(arguments.joined(separator: ", "))") + try process.run() process.waitUntilExit() let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" - dump(process) - print(output) - print(error) + Logger.subprocess.info("Process.run output: \(output)") + if !error.isEmpty { + Logger.subprocess.error("Process.run error: \(error)") + } + guard process.terminationReason == .exit, process.terminationStatus == 0 else { DispatchQueue.main.async { promise(.failure(ProcessExecutionError(process: process, standardOutput: output, standardError: error))) diff --git a/XcodesTests/Environment+Mock.swift b/XcodesTests/Environment+Mock.swift index bf24ce5..ed91706 100644 --- a/XcodesTests/Environment+Mock.swift +++ b/XcodesTests/Environment+Mock.swift @@ -7,7 +7,6 @@ extension Environment { shell: .mock, files: .mock, network: .mock, - logging: .mock, keychain: .mock, defaults: .mock, date: Date.mock, @@ -73,12 +72,6 @@ extension Network { ) } -extension Logging { - static var mock = Logging( - log: { print($0) } - ) -} - extension Keychain { static var mock = Keychain( getString: { _ in return nil }, diff --git a/com.robotsandpencils.XcodesApp.Helper/ConnectionVerifier.swift b/com.robotsandpencils.XcodesApp.Helper/ConnectionVerifier.swift index 6ad6af6..7ad5033 100644 --- a/com.robotsandpencils.XcodesApp.Helper/ConnectionVerifier.swift +++ b/com.robotsandpencils.XcodesApp.Helper/ConnectionVerifier.swift @@ -1,6 +1,7 @@ // From https://github.com/securing/SimpleXPCApp/ import Foundation +import os.log class ConnectionVerifier { @@ -12,19 +13,19 @@ class ConnectionVerifier { ] if SecCodeCopyGuestWithAttributes(nil, attributesDictrionary as CFDictionary, SecCSFlags(rawValue: 0), &secCodeOptional) != errSecSuccess { - NSLog("Couldn't get SecCode with the audit token") + Logger.connectionVerifier.error("Couldn't get SecCode with the audit token") return false } guard let secCode = secCodeOptional else { - NSLog("Couldn't unwrap the secCode") + Logger.connectionVerifier.error("Couldn't unwrap the secCode") return false } SecCodeCopyStaticCode(secCode, SecCSFlags(rawValue: 0), &secStaticCodeOptional) guard let _ = secStaticCodeOptional else { - NSLog("Couldn't unwrap the secStaticCode") + Logger.connectionVerifier.error("Couldn't unwrap the secStaticCode") return false } @@ -34,7 +35,7 @@ class ConnectionVerifier { private static func verifyHardenedRuntimeAndProblematicEntitlements(secStaticCode: SecStaticCode) -> Bool { var signingInformationOptional: CFDictionary? = nil if SecCodeCopySigningInformation(secStaticCode, SecCSFlags(rawValue: kSecCSDynamicInformation), &signingInformationOptional) != errSecSuccess { - NSLog("Couldn't obtain signing information") + Logger.connectionVerifier.error("Couldn't obtain signing information") return false } @@ -49,7 +50,7 @@ class ConnectionVerifier { if let signingFlags = signingFlagsOptional { let hardenedRuntimeFlag: UInt32 = 0x10000 if (signingFlags & hardenedRuntimeFlag) != hardenedRuntimeFlag { - NSLog("Hardened runtime is not set for the sender") + Logger.connectionVerifier.error("Hardened runtime is not set for the sender") return false } } else { @@ -60,7 +61,7 @@ class ConnectionVerifier { guard let entitlements = entitlementsOptional else { return false } - NSLog("Entitlements are \(entitlements)") + Logger.connectionVerifier.info("Entitlements are \(entitlements)") let problematicEntitlements = [ "com.apple.security.get-task-allow", "com.apple.security.cs.disable-library-validation", @@ -72,7 +73,7 @@ class ConnectionVerifier { for problematicEntitlement in problematicEntitlements { if let presentEntitlement = entitlements.object(forKey: problematicEntitlement) { if presentEntitlement as! Int == 1 { - NSLog("The sender has \(problematicEntitlement) entitlement set to true") + Logger.connectionVerifier.error("The sender has \(problematicEntitlement) entitlement set to true") return false } } @@ -89,12 +90,12 @@ class ConnectionVerifier { var secRequirement: SecRequirement? = nil if SecRequirementCreateWithString(requirementString as CFString, SecCSFlags(rawValue: 0), &secRequirement) != errSecSuccess { - NSLog("Couldn't create the requirement string") + Logger.connectionVerifier.error("Couldn't create the requirement string") return false } if SecCodeCheckValidity(secCode, SecCSFlags(rawValue: 0), secRequirement) != errSecSuccess { - NSLog("NSXPC client does not meet the requirements") + Logger.connectionVerifier.error("NSXPC client does not meet the requirements") return false } diff --git a/com.robotsandpencils.XcodesApp.Helper/Logger.swift b/com.robotsandpencils.XcodesApp.Helper/Logger.swift new file mode 100644 index 0000000..3b39654 --- /dev/null +++ b/com.robotsandpencils.XcodesApp.Helper/Logger.swift @@ -0,0 +1,9 @@ +import Foundation +import os.log + +extension Logger { + private static var subsystem = Bundle.main.bundleIdentifier! + + static let connectionVerifier = Logger(subsystem: subsystem, category: "connectionVerifier") + static let xpcDelegate = Logger(subsystem: subsystem, category: "xpcDelegate") +} diff --git a/com.robotsandpencils.XcodesApp.Helper/XPCDelegate.swift b/com.robotsandpencils.XcodesApp.Helper/XPCDelegate.swift index 37d7962..86a33301 100644 --- a/com.robotsandpencils.XcodesApp.Helper/XPCDelegate.swift +++ b/com.robotsandpencils.XcodesApp.Helper/XPCDelegate.swift @@ -1,4 +1,5 @@ import Foundation +import os.log class XPCDelegate: NSObject, NSXPCListenerDelegate, HelperXPCProtocol { @@ -16,12 +17,12 @@ class XPCDelegate: NSObject, NSXPCListenerDelegate, HelperXPCProtocol { // MARK: - HelperXPCProtocol func getVersion(completion: @escaping (String) -> Void) { - NSLog("XPCDelegate: \(#function)") + Logger.xpcDelegate.info("\(#function)") completion(Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String) } func xcodeSelect(absolutePath: String, completion: @escaping (Error?) -> Void) { - NSLog("XPCDelegate: \(#function)") + Logger.xpcDelegate.info("\(#function)") guard URL(fileURLWithPath: absolutePath).hasDirectoryPath else { completion(XPCDelegateError(.invalidXcodePath)) @@ -55,7 +56,7 @@ class XPCDelegate: NSObject, NSXPCListenerDelegate, HelperXPCProtocol { // MARK: - Run private func run(url: URL, arguments: [String], completion: @escaping (Error?) -> Void) { - NSLog("XPCDelegate: run \(url) \(arguments)") + Logger.xpcDelegate.info("Run executable: \(url), arguments: \(arguments.joined(separator: ", "))") let process = Process() process.executableURL = url