From 60b35cefb23a857535fbbba3a348a5ab81d6face Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Olejn=C3=ADk?= Date: Fri, 29 Oct 2021 15:55:18 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=80=20Prepare=20info=20output=20for=20?= =?UTF-8?q?some=20commands=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Add possibility to optionally suppress `System` output * ✨ Add logger for logging output * ✨ Suppress `ArchiveService` output * ✨ Add output to `CarthageController` calls * ✨ Add output to `AutoPrefixService` * ✨ Add info output to `Download` command * ✨ Add info output to `Upload` command --- Sources/TorinoCore/Commands/Download.swift | 19 +++++- Sources/TorinoCore/Commands/Upload.swift | 20 +++++- .../TorinoCore/Services/ArchiveService.swift | 2 +- .../Services/AutoPrefixService.swift | 17 ++++- .../Services/CarthageController.swift | 9 ++- Sources/TorinoCore/Services/Logger.swift | 35 ++++++++++ Sources/TorinoCore/Services/System.swift | 64 +++++++++++++++---- 7 files changed, 142 insertions(+), 24 deletions(-) create mode 100644 Sources/TorinoCore/Services/Logger.swift diff --git a/Sources/TorinoCore/Commands/Download.swift b/Sources/TorinoCore/Commands/Download.swift index 9a5bc6b..62e52b0 100644 --- a/Sources/TorinoCore/Commands/Download.swift +++ b/Sources/TorinoCore/Commands/Download.swift @@ -10,22 +10,35 @@ struct Download: ParsableCommand { @OptionGroup var args: SharedArguments func run() throws { + let logger = Logger.shared + guard let cwd = localFileSystem.currentWorkingDirectory else { throw UploadError(message: "Unable to get current working directory") } + let prefix = try args.prefix ?? AutoPrefixService().autoPrefix() let pathProvider = try CarthagePathProvider( base: cwd, - prefix: try args.prefix ?? AutoPrefixService().autoPrefix() + prefix: prefix ) let lockfilePath = pathProvider.lockfile() let lockfileContent = try localFileSystem.readFileContents(lockfilePath) let lockfile = Lockfile.from(string: lockfileContent.cString) - let gcpDownloader = (try? GCPConfig(environment: ProcessEnv.vars)) - .map { GCPDownloader(config: $0) } + let gcpDownloader: GCPDownloading? = { + do { + return try GCPDownloader(config: GCPConfig(environment: ProcessEnv.vars)) + } catch { + logger.error("Unable to decode GCP configuration") + logger.error(error.localizedDescription) + logger.info("Remote cache will not be used") + } + + return nil + }() + logger.info("Trying to download cached dependencies with prefix", prefix) try LocalDependenciesDownloader(gcpDownloader: gcpDownloader, pathProvider: pathProvider) .downloadDependencies( dependencies: lockfile.dependencies.map { diff --git a/Sources/TorinoCore/Commands/Upload.swift b/Sources/TorinoCore/Commands/Upload.swift index ed8bf78..3db149b 100644 --- a/Sources/TorinoCore/Commands/Upload.swift +++ b/Sources/TorinoCore/Commands/Upload.swift @@ -10,17 +10,31 @@ struct Upload: ParsableCommand { @OptionGroup var args: SharedArguments func run() throws { + let logger = Logger.shared + guard let cwd = localFileSystem.currentWorkingDirectory else { throw UploadError(message: "Unable to get current working directory") } + let prefix = try args.prefix ?? AutoPrefixService().autoPrefix() let pathProvider = try CarthagePathProvider( base: cwd, - prefix: try args.prefix ?? AutoPrefixService().autoPrefix() + prefix: prefix ) - let gcpUploader = (try? GCPConfig(environment: ProcessEnv.vars)) - .map { GCPUploader(config: $0) } + let gcpUploader: GCPUploading? = { + do { + return try GCPUploader(config: GCPConfig(environment: ProcessEnv.vars)) + } catch { + logger.error("Unable to decode GCP configuration") + logger.error(error.localizedDescription) + logger.info("Remote cache will not be used") + } + + return nil + }() + + logger.info("Trying to upload cached dependencies with prefix", prefix) try UploadService( dependenciesLoader: CarthageDependenciesLoader(pathProvider: pathProvider), diff --git a/Sources/TorinoCore/Services/ArchiveService.swift b/Sources/TorinoCore/Services/ArchiveService.swift index 2f85e57..cc3b7b7 100644 --- a/Sources/TorinoCore/Services/ArchiveService.swift +++ b/Sources/TorinoCore/Services/ArchiveService.swift @@ -40,6 +40,6 @@ final class ZipService: ArchiveServicing { // MARK: - Private helpers private func shell(_ args: [String], cwd: AbsolutePath? = nil) throws { - try system.run(args, cwd: cwd) + try system.run(args, cwd: cwd, suppressOutput: true) } } diff --git a/Sources/TorinoCore/Services/AutoPrefixService.swift b/Sources/TorinoCore/Services/AutoPrefixService.swift index 7ef4aa6..509dc31 100644 --- a/Sources/TorinoCore/Services/AutoPrefixService.swift +++ b/Sources/TorinoCore/Services/AutoPrefixService.swift @@ -10,17 +10,24 @@ struct AutoPrefixError: Error { struct AutoPrefixService: AutoPrefixServicing { private let system: Systeming + private let logger: Logging // MARK: - Initializers - init(system: Systeming = System.shared) { + init( + system: Systeming = System.shared, + logger: Logging = Logger.shared + ) { self.system = system + self.logger = logger } // MARK: - Interface func autoPrefix() throws -> String { - let swiftVersion = try system.run("swift", "-version") + logger.info("Trying to automatically detect cache prefix") + + let swiftVersion = try system.run("swift", "-version", suppressOutput: true) let regex = try NSRegularExpression( pattern: #"Swift Version ([0-9]+\.[0-9]+(\.[0-9]+)?)"#, @@ -37,7 +44,11 @@ struct AutoPrefixService: AutoPrefixServicing { if matchRange == result.range { continue } - return "Swift-" + (swiftVersion as NSString).substring(with: matchRange) + let prefix = "Swift-" + (swiftVersion as NSString).substring(with: matchRange) + + logger.info("Detected cache prefix is", prefix) + + return prefix } } diff --git a/Sources/TorinoCore/Services/CarthageController.swift b/Sources/TorinoCore/Services/CarthageController.swift index 7c6ad30..b6edaf1 100644 --- a/Sources/TorinoCore/Services/CarthageController.swift +++ b/Sources/TorinoCore/Services/CarthageController.swift @@ -8,11 +8,16 @@ protocol CarthageControlling { struct CarthageController: CarthageControlling { private let system: Systeming + private let logger: Logging // MARK: - Initializers - init(system: Systeming = System.shared) { + init( + system: Systeming = System.shared, + logger: Logging = Logger.shared + ) { self.system = system + self.logger = logger } // MARK: - Interface @@ -23,6 +28,7 @@ struct CarthageController: CarthageControlling { func update(_ args: CarthageArguments, noBuild: Bool) throws { if noBuild { + logger.info("Updating Carthage dependencies without building") try system.run("carthage", "update", "--no-build") } else { try buildUsing(command: "update", args: args) @@ -44,6 +50,7 @@ struct CarthageController: CarthageControlling { command += ["--platform", platform] } + logger.info("Running `\(command.joined(separator: " "))`") try system.run(command) } } diff --git a/Sources/TorinoCore/Services/Logger.swift b/Sources/TorinoCore/Services/Logger.swift new file mode 100644 index 0000000..6ae2d7e --- /dev/null +++ b/Sources/TorinoCore/Services/Logger.swift @@ -0,0 +1,35 @@ +import Foundation +import TSCUtility + +protocol Logging { + func logStdout(_ output: String...) + func info(_ info: String...) +} + +struct Logger: Logging { + static let shared = Logger() + + private init() { + + } + + func logStdout(_ output: String...) { + print(type: "[OUTPUT]", output: output) + } + + func info(_ info: String...) { + print(type: "[INFO]", output: info) + } + + func error(_ error: String...) { + print(type: "[ERROR]", output: error) + } + + private func print(type: String, output: [String]) { + Swift.print( + type, + output.map { $0.trimmingCharacters(in: .newlines).replacingOccurrences(of: "\n", with: "\n" + type) } + .joined(separator: " ") + ) + } +} diff --git a/Sources/TorinoCore/Services/System.swift b/Sources/TorinoCore/Services/System.swift index 17be4e9..1ee64ee 100644 --- a/Sources/TorinoCore/Services/System.swift +++ b/Sources/TorinoCore/Services/System.swift @@ -3,43 +3,81 @@ import TSCBasic protocol Systeming { @discardableResult - func run(_ arguments: [String], cwd: AbsolutePath?) throws -> String + func run(_ arguments: [String], cwd: AbsolutePath?, suppressOutput: Bool) throws -> String } extension Systeming { @discardableResult - func run(_ arguments: String..., cwd: AbsolutePath? = nil) throws -> String { - try run(Array(arguments), cwd: cwd) + func run(_ arguments: String..., cwd: AbsolutePath? = nil, suppressOutput: Bool = false) throws -> String { + try run(Array(arguments), cwd: cwd, suppressOutput: suppressOutput) } @discardableResult func run(_ arguments: [String]) throws -> String { - try run(arguments, cwd: nil) + try run(arguments, cwd: nil, suppressOutput: false) } } struct SystemError: Error { + enum Reason { + case terminated + case signal + } + let code: Int32 + let reason: Reason + let stdOut: String + let stdErr: String } struct System: Systeming { - static let shared = System() + static let shared = System(logger: Logger.shared) - private init() { - + private init(logger: Logging) { + self.logger = logger } + private let logger: Logging + @discardableResult - func run(_ args: [String], cwd: AbsolutePath?) throws -> String { + func run(_ args: [String], cwd: AbsolutePath?, suppressOutput: Bool) throws -> String { + var stdout = "" + var stderr = "" + let task: TSCBasic.Process = { + let outputRedirection: TSCBasic.Process.OutputRedirection = .stream( + stdout: { output in + let newOutput = String(decoding: output, as: Unicode.UTF8.self) + + stdout += newOutput + + if !suppressOutput { + logger.logStdout(newOutput) + } + }, + stderr: { output in + let newOutput = String(decoding: output, as: Unicode.UTF8.self) + + stderr += newOutput + + if !suppressOutput { + logger.logStdout(newOutput) + } + }, + redirectStderr: false + ) + if let cwd = cwd { return Process( arguments: args, workingDirectory: cwd, - outputRedirection: .collect + outputRedirection: outputRedirection ) } - return Process(arguments: args) + return Process( + arguments: args, + outputRedirection: outputRedirection + ) }() try task.launch() @@ -48,11 +86,11 @@ struct System: Systeming { switch result.exitStatus { case .signalled(signal: let signal): // Don't care about code/signal - throw SystemError(code: signal) + throw SystemError(code: signal, reason: .signal, stdOut: stdout, stdErr: stderr) case .terminated(code: let code) where code != 0: - throw SystemError(code: code) + throw SystemError(code: code, reason: .terminated, stdOut: stdout, stdErr: stderr) case .terminated: - return try result.utf8Output() + return stdout } } }