feat: Updates logging configuration.
This commit is contained in:
@@ -40,7 +40,8 @@ let package = Package(
|
|||||||
"ConfigurationClient",
|
"ConfigurationClient",
|
||||||
"FileClient",
|
"FileClient",
|
||||||
"GitClient",
|
"GitClient",
|
||||||
.product(name: "Logging", package: "swift-log")
|
.product(name: "Logging", package: "swift-log"),
|
||||||
|
.product(name: "CustomDump", package: "swift-custom-dump")
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
|
|||||||
@@ -34,12 +34,30 @@ public struct CliClient: Sendable {
|
|||||||
case major, minor, patch, preRelease
|
case major, minor, patch, preRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Need a quiet option, as default log level is warning, need a way to set it to ignore logs.
|
||||||
|
public struct LoggingOptions: Equatable, Sendable {
|
||||||
|
|
||||||
|
let command: String
|
||||||
|
let executableName: String
|
||||||
|
let verbose: Int
|
||||||
|
|
||||||
|
public init(
|
||||||
|
executableName: String = "bump-version",
|
||||||
|
command: String,
|
||||||
|
verbose: Int
|
||||||
|
) {
|
||||||
|
self.executableName = executableName
|
||||||
|
self.command = command
|
||||||
|
self.verbose = verbose
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct SharedOptions: Equatable, Sendable {
|
public struct SharedOptions: Equatable, Sendable {
|
||||||
|
|
||||||
let allowPreReleaseTag: Bool
|
let allowPreReleaseTag: Bool
|
||||||
let dryRun: Bool
|
let dryRun: Bool
|
||||||
let gitDirectory: String?
|
let gitDirectory: String?
|
||||||
let logLevel: Logger.Level
|
let loggingOptions: LoggingOptions
|
||||||
let target: Configuration.Target?
|
let target: Configuration.Target?
|
||||||
let branch: Configuration.Branch?
|
let branch: Configuration.Branch?
|
||||||
let semvar: Configuration.SemVar?
|
let semvar: Configuration.SemVar?
|
||||||
@@ -49,7 +67,7 @@ public struct CliClient: Sendable {
|
|||||||
allowPreReleaseTag: Bool = true,
|
allowPreReleaseTag: Bool = true,
|
||||||
dryRun: Bool = false,
|
dryRun: Bool = false,
|
||||||
gitDirectory: String? = nil,
|
gitDirectory: String? = nil,
|
||||||
logLevel: Logger.Level = .debug,
|
loggingOptions: LoggingOptions,
|
||||||
target: Configuration.Target? = nil,
|
target: Configuration.Target? = nil,
|
||||||
branch: Configuration.Branch? = nil,
|
branch: Configuration.Branch? = nil,
|
||||||
semvar: Configuration.SemVar? = nil,
|
semvar: Configuration.SemVar? = nil,
|
||||||
@@ -58,34 +76,12 @@ public struct CliClient: Sendable {
|
|||||||
self.allowPreReleaseTag = allowPreReleaseTag
|
self.allowPreReleaseTag = allowPreReleaseTag
|
||||||
self.dryRun = dryRun
|
self.dryRun = dryRun
|
||||||
self.gitDirectory = gitDirectory
|
self.gitDirectory = gitDirectory
|
||||||
self.logLevel = logLevel
|
self.loggingOptions = loggingOptions
|
||||||
self.target = target
|
self.target = target
|
||||||
self.branch = branch
|
self.branch = branch
|
||||||
self.semvar = semvar
|
self.semvar = semvar
|
||||||
self.configurationFile = configurationFile
|
self.configurationFile = configurationFile
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(
|
|
||||||
allowPreReleaseTag: Bool = true,
|
|
||||||
dryRun: Bool = false,
|
|
||||||
gitDirectory: String? = nil,
|
|
||||||
verbose: Int,
|
|
||||||
target: Configuration.Target? = nil,
|
|
||||||
branch: Configuration.Branch? = nil,
|
|
||||||
semvar: Configuration.SemVar? = nil,
|
|
||||||
configurationFile: String? = nil
|
|
||||||
) {
|
|
||||||
self.init(
|
|
||||||
allowPreReleaseTag: allowPreReleaseTag,
|
|
||||||
dryRun: dryRun,
|
|
||||||
gitDirectory: gitDirectory,
|
|
||||||
logLevel: .init(verbose: verbose),
|
|
||||||
target: target,
|
|
||||||
branch: branch,
|
|
||||||
semvar: semvar,
|
|
||||||
configurationFile: configurationFile
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ enum CliClientError: Error {
|
|||||||
case fileDoesNotExist(path: String)
|
case fileDoesNotExist(path: String)
|
||||||
case failedToParseVersionFile
|
case failedToParseVersionFile
|
||||||
case semVarNotFound
|
case semVarNotFound
|
||||||
|
case preReleaseParsingError(String)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import ConfigurationClient
|
import ConfigurationClient
|
||||||
|
import CustomDump
|
||||||
import Dependencies
|
import Dependencies
|
||||||
import FileClient
|
import FileClient
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -7,6 +8,40 @@ import GitClient
|
|||||||
@_spi(Internal)
|
@_spi(Internal)
|
||||||
public extension CliClient.SharedOptions {
|
public extension CliClient.SharedOptions {
|
||||||
|
|
||||||
|
/// All cli-client calls should run through this, it set's up logging,
|
||||||
|
/// loads configuration, and generates the current version based on the
|
||||||
|
/// configuration.
|
||||||
|
@discardableResult
|
||||||
|
func run(
|
||||||
|
_ operation: (CurrentVersionContainer) async throws -> Void
|
||||||
|
) async rethrows -> String {
|
||||||
|
try await loggingOptions.withLogger {
|
||||||
|
// Load the default configuration, if it exists.
|
||||||
|
try await withMergedConfiguration { configuration in
|
||||||
|
@Dependency(\.logger) var logger
|
||||||
|
|
||||||
|
var configurationString = ""
|
||||||
|
customDump(configuration, to: &configurationString)
|
||||||
|
logger.trace("\nConfiguration: \(configurationString)")
|
||||||
|
|
||||||
|
// This will fail if the target url is not set properly.
|
||||||
|
let targetUrl = try configuration.targetUrl(gitDirectory: gitDirectory)
|
||||||
|
logger.debug("Target: \(targetUrl.cleanFilePath)")
|
||||||
|
|
||||||
|
// Perform the operation, which generates the new version and writes it.
|
||||||
|
try await operation(
|
||||||
|
configuration.currentVersion(
|
||||||
|
targetUrl: targetUrl,
|
||||||
|
gitDirectory: gitDirectory
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return the file path we wrote the version to.
|
||||||
|
return targetUrl.cleanFilePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Merges any configuration set via the passed in options.
|
// Merges any configuration set via the passed in options.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func withMergedConfiguration<T>(
|
func withMergedConfiguration<T>(
|
||||||
@@ -26,37 +61,6 @@ public extension CliClient.SharedOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
func run(
|
|
||||||
_ operation: (CurrentVersionContainer) async throws -> Void
|
|
||||||
) async rethrows -> String {
|
|
||||||
try await withDependencies {
|
|
||||||
$0.logger.logLevel = logLevel
|
|
||||||
} operation: {
|
|
||||||
// Load the default configuration, if it exists.
|
|
||||||
try await withMergedConfiguration { configuration in
|
|
||||||
@Dependency(\.logger) var logger
|
|
||||||
|
|
||||||
logger.debug("Configuration: \(configuration)")
|
|
||||||
|
|
||||||
// This will fail if the target url is not set properly.
|
|
||||||
let targetUrl = try configuration.targetUrl(gitDirectory: gitDirectory)
|
|
||||||
logger.debug("Target: \(targetUrl.cleanFilePath)")
|
|
||||||
|
|
||||||
// Perform the operation, which generates the new version and writes it.
|
|
||||||
try await operation(
|
|
||||||
configuration.currentVersion(
|
|
||||||
targetUrl: targetUrl,
|
|
||||||
gitDirectory: gitDirectory
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Return the file path we wrote the version to.
|
|
||||||
return targetUrl.cleanFilePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func write(_ string: String, to url: URL) async throws {
|
func write(_ string: String, to url: URL) async throws {
|
||||||
@Dependency(\.fileClient) var fileClient
|
@Dependency(\.fileClient) var fileClient
|
||||||
@Dependency(\.logger) var logger
|
@Dependency(\.logger) var logger
|
||||||
@@ -64,7 +68,7 @@ public extension CliClient.SharedOptions {
|
|||||||
try await fileClient.write(string: string, to: url)
|
try await fileClient.write(string: string, to: url)
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Skipping, due to dry-run being passed.")
|
logger.debug("Skipping, due to dry-run being passed.")
|
||||||
logger.debug("\(string)")
|
logger.debug("\n\(string)\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,6 +20,15 @@ extension Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Configuration.PreRelease {
|
||||||
|
func merging(_ other: Self?) -> Self {
|
||||||
|
.init(
|
||||||
|
prefix: other?.prefix ?? prefix,
|
||||||
|
strategy: other?.strategy ?? strategy
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Configuration.Branch {
|
extension Configuration.Branch {
|
||||||
func merging(_ other: Self?) -> Self {
|
func merging(_ other: Self?) -> Self {
|
||||||
return .init(includeCommitSha: other?.includeCommitSha ?? includeCommitSha)
|
return .init(includeCommitSha: other?.includeCommitSha ?? includeCommitSha)
|
||||||
@@ -29,7 +38,7 @@ extension Configuration.Branch {
|
|||||||
extension Configuration.SemVar {
|
extension Configuration.SemVar {
|
||||||
func merging(_ other: Self?) -> Self {
|
func merging(_ other: Self?) -> Self {
|
||||||
.init(
|
.init(
|
||||||
preRelease: other?.preRelease ?? preRelease,
|
preRelease: preRelease?.merging(other?.preRelease),
|
||||||
requireExistingFile: other?.requireExistingFile ?? requireExistingFile,
|
requireExistingFile: other?.requireExistingFile ?? requireExistingFile,
|
||||||
requireExistingSemVar: other?.requireExistingSemVar ?? requireExistingSemVar
|
requireExistingSemVar: other?.requireExistingSemVar ?? requireExistingSemVar
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import ConfigurationClient
|
|||||||
import Dependencies
|
import Dependencies
|
||||||
import Foundation
|
import Foundation
|
||||||
import GitClient
|
import GitClient
|
||||||
|
import ShellClient
|
||||||
|
|
||||||
extension Configuration {
|
extension Configuration {
|
||||||
func targetUrl(gitDirectory: String?) throws -> URL {
|
func targetUrl(gitDirectory: String?) throws -> URL {
|
||||||
@@ -58,26 +59,46 @@ extension Configuration.Target {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension GitClient {
|
extension GitClient {
|
||||||
func version(branch: Configuration.Branch, gitDirectory: String?) async throws -> String {
|
func version(includeCommitSha: Bool, gitDirectory: String?) async throws -> String {
|
||||||
@Dependency(\.gitClient) var gitClient
|
@Dependency(\.gitClient) var gitClient
|
||||||
|
|
||||||
return try await gitClient.version(.init(
|
return try await gitClient.version(.init(
|
||||||
gitDirectory: gitDirectory,
|
gitDirectory: gitDirectory,
|
||||||
style: .branch(commitSha: branch.includeCommitSha)
|
style: .branch(commitSha: includeCommitSha)
|
||||||
)).description
|
)).description
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Configuration.PreRelease {
|
extension Configuration.PreRelease {
|
||||||
|
|
||||||
func preReleaseString(gitDirectory: String?) async throws -> String {
|
// FIX: This needs to handle the pre-release type appropriatly.
|
||||||
|
func preReleaseString(gitDirectory: String?) async throws -> PreReleaseString? {
|
||||||
|
guard let strategy else { return nil }
|
||||||
|
|
||||||
|
@Dependency(\.asyncShellClient) var asyncShellClient
|
||||||
@Dependency(\.gitClient) var gitClient
|
@Dependency(\.gitClient) var gitClient
|
||||||
|
@Dependency(\.logger) var logger
|
||||||
|
|
||||||
let preReleaseString: String
|
var preReleaseString: String
|
||||||
|
var suffix = true
|
||||||
|
var allowsPrefix = true
|
||||||
|
|
||||||
if let branch = strategy?.branch {
|
switch strategy {
|
||||||
preReleaseString = try await gitClient.version(branch: branch, gitDirectory: gitDirectory)
|
case let .branch(includeCommitSha: includeCommitSha):
|
||||||
} else {
|
logger.trace("Branch pre-release strategy, includeCommitSha: \(includeCommitSha).")
|
||||||
|
preReleaseString = try await gitClient.version(
|
||||||
|
includeCommitSha: includeCommitSha,
|
||||||
|
gitDirectory: gitDirectory
|
||||||
|
)
|
||||||
|
case let .command(arguments: arguments):
|
||||||
|
logger.trace("Command pre-release strategy, arguments: \(arguments).")
|
||||||
|
// TODO: What to do with allows prefix? Need a configuration setting for commands.
|
||||||
|
preReleaseString = try await asyncShellClient.background(.init(arguments))
|
||||||
|
case .gitTag:
|
||||||
|
logger.trace("Git tag pre-release strategy.")
|
||||||
|
logger.trace("This will ignore any set prefix.")
|
||||||
|
suffix = false
|
||||||
|
allowsPrefix = false
|
||||||
preReleaseString = try await gitClient.version(.init(
|
preReleaseString = try await gitClient.version(.init(
|
||||||
gitDirectory: gitDirectory,
|
gitDirectory: gitDirectory,
|
||||||
style: .tag(exactMatch: false)
|
style: .tag(exactMatch: false)
|
||||||
@@ -85,9 +106,22 @@ extension Configuration.PreRelease {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let prefix {
|
if let prefix {
|
||||||
return "\(prefix)-\(preReleaseString)"
|
if allowsPrefix {
|
||||||
|
preReleaseString = "\(prefix)-\(preReleaseString)"
|
||||||
|
} else {
|
||||||
|
logger.warning("Found prefix, but pre-release strategy may not work properly, ignoring prefix.")
|
||||||
}
|
}
|
||||||
return preReleaseString
|
}
|
||||||
|
|
||||||
|
guard suffix else { return .semvar(preReleaseString) }
|
||||||
|
return .suffix(preReleaseString)
|
||||||
|
|
||||||
|
// return preReleaseString
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PreReleaseString: Sendable {
|
||||||
|
case suffix(String)
|
||||||
|
case semvar(String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,15 +131,28 @@ public extension Configuration.SemVar {
|
|||||||
private func applyingPreRelease(_ semVar: SemVar, _ gitDirectory: String?) async throws -> SemVar {
|
private func applyingPreRelease(_ semVar: SemVar, _ gitDirectory: String?) async throws -> SemVar {
|
||||||
@Dependency(\.logger) var logger
|
@Dependency(\.logger) var logger
|
||||||
logger.trace("Start apply pre-release to: \(semVar)")
|
logger.trace("Start apply pre-release to: \(semVar)")
|
||||||
guard let preReleaseStrategy = self.preRelease else {
|
|
||||||
|
guard let preReleaseStrategy = self.preRelease,
|
||||||
|
let preRelease = try await preReleaseStrategy.preReleaseString(gitDirectory: gitDirectory)
|
||||||
|
else {
|
||||||
logger.trace("No pre-release strategy, returning original semvar.")
|
logger.trace("No pre-release strategy, returning original semvar.")
|
||||||
return semVar
|
return semVar
|
||||||
}
|
}
|
||||||
|
|
||||||
let preRelease = try await preReleaseStrategy.preReleaseString(gitDirectory: gitDirectory)
|
// let preRelease = try await preReleaseStrategy.preReleaseString(gitDirectory: gitDirectory)
|
||||||
logger.trace("Pre-release string: \(preRelease)")
|
logger.trace("Pre-release string: \(preRelease)")
|
||||||
|
|
||||||
return semVar.applyingPreRelease(preRelease)
|
switch preRelease {
|
||||||
|
case let .suffix(string):
|
||||||
|
return semVar.applyingPreRelease(string)
|
||||||
|
case let .semvar(string):
|
||||||
|
guard let semvar = SemVar(string: string) else {
|
||||||
|
throw CliClientError.preReleaseParsingError(string)
|
||||||
|
}
|
||||||
|
return semvar
|
||||||
|
}
|
||||||
|
|
||||||
|
// return semVar.applyingPreRelease(preRelease)
|
||||||
}
|
}
|
||||||
|
|
||||||
func currentVersion(file: URL, gitDirectory: String? = nil) async throws -> CurrentVersionContainer.Version {
|
func currentVersion(file: URL, gitDirectory: String? = nil) async throws -> CurrentVersionContainer.Version {
|
||||||
@@ -181,7 +228,7 @@ extension Configuration.VersionStrategy {
|
|||||||
return try await .init(
|
return try await .init(
|
||||||
targetUrl: targetUrl,
|
targetUrl: targetUrl,
|
||||||
version: .string(
|
version: .string(
|
||||||
gitClient.version(branch: branch, gitDirectory: gitDirectory)
|
gitClient.version(includeCommitSha: branch.includeCommitSha, gitDirectory: gitDirectory)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ public extension FileClient {
|
|||||||
logger.debug("Version line: \(versionLine)")
|
logger.debug("Version line: \(versionLine)")
|
||||||
|
|
||||||
let isOptional = versionLine.contains("String?")
|
let isOptional = versionLine.contains("String?")
|
||||||
logger.debug("Uses optional: \(isOptional)")
|
logger.trace("Uses optional: \(isOptional)")
|
||||||
|
|
||||||
let versionString = versionLine.split(separator: "let VERSION: \(isOptional ? "String?" : "String") = ").last
|
let versionString = versionLine.split(separator: "let VERSION: \(isOptional ? "String?" : "String") = ").last
|
||||||
guard let versionString else {
|
guard let versionString else {
|
||||||
throw CliClientError.failedToParseVersionFile
|
throw CliClientError.failedToParseVersionFile
|
||||||
}
|
}
|
||||||
logger.debug("Parsed version string: \(versionString)")
|
logger.trace("Parsed version string: \(versionString)")
|
||||||
return (String(versionString), isOptional)
|
return (String(versionString), isOptional)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import Logging
|
|
||||||
|
|
||||||
// TODO: Move.
|
|
||||||
@_spi(Internal)
|
|
||||||
public extension Logger.Level {
|
|
||||||
|
|
||||||
init(verbose: Int) {
|
|
||||||
switch verbose {
|
|
||||||
case 1: self = .warning
|
|
||||||
case 2: self = .debug
|
|
||||||
case 3...: self = .trace
|
|
||||||
default: self = .info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
172
Sources/CliClient/Internal/Logging.swift
Normal file
172
Sources/CliClient/Internal/Logging.swift
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import Dependencies
|
||||||
|
import Foundation
|
||||||
|
import Logging
|
||||||
|
import LoggingFormatAndPipe
|
||||||
|
import Rainbow
|
||||||
|
import ShellClient
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
var orange: Self {
|
||||||
|
bit24(255, 165, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var magena: Self {
|
||||||
|
// bit24(186, 85, 211)
|
||||||
|
bit24(238, 130, 238)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@_spi(Internal)
|
||||||
|
public extension Logger.Level {
|
||||||
|
|
||||||
|
init(verbose: Int) {
|
||||||
|
switch verbose {
|
||||||
|
case 1: self = .debug
|
||||||
|
case 2...: self = .trace
|
||||||
|
default: self = .warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var coloredString: String {
|
||||||
|
switch self {
|
||||||
|
case .info:
|
||||||
|
return "\(self)".cyan
|
||||||
|
case .warning:
|
||||||
|
return "\(self)".orange.bold
|
||||||
|
case .debug:
|
||||||
|
return "\(self)".green
|
||||||
|
case .trace:
|
||||||
|
return "\(self)".yellow
|
||||||
|
case .error:
|
||||||
|
return "\(self)".red.bold
|
||||||
|
default:
|
||||||
|
return "\(self)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LevelFormatter: LoggingFormatAndPipe.Formatter {
|
||||||
|
|
||||||
|
let basic: BasicFormatter
|
||||||
|
|
||||||
|
var timestampFormatter: DateFormatter { basic.timestampFormatter }
|
||||||
|
|
||||||
|
// swiftlint:disable function_parameter_count
|
||||||
|
func processLog(
|
||||||
|
level: Logger.Level,
|
||||||
|
message: Logger.Message,
|
||||||
|
prettyMetadata: String?,
|
||||||
|
file: String,
|
||||||
|
function: String,
|
||||||
|
line: UInt
|
||||||
|
) -> String {
|
||||||
|
let now = Date()
|
||||||
|
|
||||||
|
return basic.format.map { component -> String in
|
||||||
|
return processComponent(
|
||||||
|
component,
|
||||||
|
now: now,
|
||||||
|
level: level,
|
||||||
|
message: message,
|
||||||
|
prettyMetadata: prettyMetadata,
|
||||||
|
file: file,
|
||||||
|
function: function,
|
||||||
|
line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.filter { $0.count > 0 }
|
||||||
|
.joined(separator: basic.separator ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func processComponent(
|
||||||
|
_ component: LogComponent,
|
||||||
|
now: Date,
|
||||||
|
level: Logger.Level,
|
||||||
|
message: Logger.Message,
|
||||||
|
prettyMetadata: String?,
|
||||||
|
file: String,
|
||||||
|
function: String,
|
||||||
|
line: UInt
|
||||||
|
) -> String {
|
||||||
|
switch component {
|
||||||
|
case .level:
|
||||||
|
let maxLen = "\(Logger.Level.warning)".count
|
||||||
|
let paddingCount = (maxLen - "\(level)".count) / 2
|
||||||
|
var padding = ""
|
||||||
|
for _ in 0 ... paddingCount {
|
||||||
|
padding += " "
|
||||||
|
}
|
||||||
|
return "\(padding)\(level.coloredString)\(padding)"
|
||||||
|
case let .group(components):
|
||||||
|
return components.map { component -> String in
|
||||||
|
self.processComponent(
|
||||||
|
component,
|
||||||
|
now: now,
|
||||||
|
level: level,
|
||||||
|
message: message,
|
||||||
|
prettyMetadata: prettyMetadata,
|
||||||
|
file: file,
|
||||||
|
function: function,
|
||||||
|
line: line
|
||||||
|
)
|
||||||
|
}.joined()
|
||||||
|
case .message:
|
||||||
|
return basic.processComponent(
|
||||||
|
component,
|
||||||
|
now: now,
|
||||||
|
level: level,
|
||||||
|
message: message,
|
||||||
|
prettyMetadata: prettyMetadata,
|
||||||
|
file: file,
|
||||||
|
function: function,
|
||||||
|
line: line
|
||||||
|
).italic
|
||||||
|
default:
|
||||||
|
return basic.processComponent(
|
||||||
|
component,
|
||||||
|
now: now,
|
||||||
|
level: level,
|
||||||
|
message: message,
|
||||||
|
prettyMetadata: prettyMetadata,
|
||||||
|
file: file,
|
||||||
|
function: function,
|
||||||
|
line: line
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// swiftlint:enable function_parameter_count
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CliClient.LoggingOptions {
|
||||||
|
|
||||||
|
func makeLogger() -> Logger {
|
||||||
|
let formatters: [LogComponent] = [
|
||||||
|
.text(executableName.magena),
|
||||||
|
.text(command.blue),
|
||||||
|
.level,
|
||||||
|
.group([
|
||||||
|
.text("-> "),
|
||||||
|
.message
|
||||||
|
])
|
||||||
|
]
|
||||||
|
return Logger(label: executableName) { _ in
|
||||||
|
LoggingFormatAndPipe.Handler(
|
||||||
|
formatter: LevelFormatter(basic: BasicFormatter(
|
||||||
|
formatters,
|
||||||
|
separator: " | "
|
||||||
|
)),
|
||||||
|
pipe: LoggerTextOutputStreamPipe.standardOutput
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func withLogger<T>(_ operation: () async throws -> T) async rethrows -> T {
|
||||||
|
try await withDependencies {
|
||||||
|
$0.logger = makeLogger()
|
||||||
|
$0.logger.logLevel = .init(verbose: verbose)
|
||||||
|
} operation: {
|
||||||
|
try await operation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,10 @@ import Foundation
|
|||||||
import ShellClient
|
import ShellClient
|
||||||
|
|
||||||
struct BuildCommand: AsyncParsableCommand {
|
struct BuildCommand: AsyncParsableCommand {
|
||||||
|
static let commandName = "build"
|
||||||
|
|
||||||
static let configuration: CommandConfiguration = .init(
|
static let configuration: CommandConfiguration = .init(
|
||||||
commandName: "build",
|
commandName: Self.commandName,
|
||||||
abstract: "Used for the build with version plugin.",
|
abstract: "Used for the build with version plugin.",
|
||||||
discussion: "This should generally not be interacted with directly, outside of the build plugin.",
|
discussion: "This should generally not be interacted with directly, outside of the build plugin.",
|
||||||
shouldDisplay: false
|
shouldDisplay: false
|
||||||
@@ -14,6 +16,6 @@ struct BuildCommand: AsyncParsableCommand {
|
|||||||
@OptionGroup var globals: GlobalOptions
|
@OptionGroup var globals: GlobalOptions
|
||||||
|
|
||||||
func run() async throws {
|
func run() async throws {
|
||||||
try await globals.run(\.build)
|
try await globals.run(\.build, command: Self.commandName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import Dependencies
|
|||||||
|
|
||||||
struct BumpCommand: AsyncParsableCommand {
|
struct BumpCommand: AsyncParsableCommand {
|
||||||
|
|
||||||
|
static let commandName = "bump"
|
||||||
|
|
||||||
static let configuration = CommandConfiguration(
|
static let configuration = CommandConfiguration(
|
||||||
commandName: "bump",
|
commandName: Self.commandName,
|
||||||
abstract: "Bump version of a command-line tool."
|
abstract: "Bump version of a command-line tool."
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +21,7 @@ struct BumpCommand: AsyncParsableCommand {
|
|||||||
var bumpOption: CliClient.BumpOption = .patch
|
var bumpOption: CliClient.BumpOption = .patch
|
||||||
|
|
||||||
func run() async throws {
|
func run() async throws {
|
||||||
try await globals.run(\.bump, args: bumpOption)
|
try await globals.run(\.bump, command: Self.commandName, args: bumpOption)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import Foundation
|
|||||||
import ShellClient
|
import ShellClient
|
||||||
|
|
||||||
struct GenerateCommand: AsyncParsableCommand {
|
struct GenerateCommand: AsyncParsableCommand {
|
||||||
|
static let commandName = "generate"
|
||||||
|
|
||||||
static let configuration: CommandConfiguration = .init(
|
static let configuration: CommandConfiguration = .init(
|
||||||
commandName: "generate",
|
commandName: Self.commandName,
|
||||||
abstract: "Generates a version file in a command line tool that can be set via the git tag or git sha.",
|
abstract: "Generates a version file in a command line tool that can be set via the git tag or git sha.",
|
||||||
discussion: "This command can be interacted with directly, outside of the plugin usage context."
|
discussion: "This command can be interacted with directly, outside of the plugin usage context."
|
||||||
)
|
)
|
||||||
@@ -14,6 +16,6 @@ struct GenerateCommand: AsyncParsableCommand {
|
|||||||
@OptionGroup var globals: GlobalOptions
|
@OptionGroup var globals: GlobalOptions
|
||||||
|
|
||||||
func run() async throws {
|
func run() async throws {
|
||||||
try await globals.run(\.generate)
|
try await globals.run(\.generate, command: Self.commandName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ struct UtilsCommand: AsyncParsableCommand {
|
|||||||
|
|
||||||
extension UtilsCommand {
|
extension UtilsCommand {
|
||||||
struct DumpConfig: AsyncParsableCommand {
|
struct DumpConfig: AsyncParsableCommand {
|
||||||
|
static let commandName = "dump-config"
|
||||||
|
|
||||||
static let configuration = CommandConfiguration(
|
static let configuration = CommandConfiguration(
|
||||||
commandName: "dump-config",
|
commandName: Self.commandName,
|
||||||
abstract: "Show the parsed configuration.",
|
abstract: "Show the parsed configuration.",
|
||||||
aliases: ["dc"]
|
aliases: ["dc"]
|
||||||
)
|
)
|
||||||
@@ -27,7 +29,7 @@ extension UtilsCommand {
|
|||||||
@OptionGroup var globals: GlobalOptions
|
@OptionGroup var globals: GlobalOptions
|
||||||
|
|
||||||
func run() async throws {
|
func run() async throws {
|
||||||
let configuration = try await globals.runClient(\.parsedConfiguration)
|
let configuration = try await globals.runClient(\.parsedConfiguration, command: Self.commandName)
|
||||||
customDump(configuration)
|
customDump(configuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,45 +21,49 @@ func withSetupDependencies<T>(
|
|||||||
extension GlobalOptions {
|
extension GlobalOptions {
|
||||||
|
|
||||||
func runClient<T>(
|
func runClient<T>(
|
||||||
_ keyPath: KeyPath<CliClient, @Sendable (CliClient.SharedOptions) async throws -> T>
|
_ keyPath: KeyPath<CliClient, @Sendable (CliClient.SharedOptions) async throws -> T>,
|
||||||
|
command: String
|
||||||
) async throws -> T {
|
) async throws -> T {
|
||||||
try await withSetupDependencies {
|
try await withSetupDependencies {
|
||||||
@Dependency(\.cliClient) var cliClient
|
@Dependency(\.cliClient) var cliClient
|
||||||
return try await cliClient[keyPath: keyPath](shared())
|
return try await cliClient[keyPath: keyPath](shared(command: command))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runClient<A, T>(
|
func runClient<A, T>(
|
||||||
_ keyPath: KeyPath<CliClient, @Sendable (A, CliClient.SharedOptions) async throws -> T>,
|
_ keyPath: KeyPath<CliClient, @Sendable (A, CliClient.SharedOptions) async throws -> T>,
|
||||||
|
command: String,
|
||||||
args: A
|
args: A
|
||||||
) async throws -> T {
|
) async throws -> T {
|
||||||
try await withSetupDependencies {
|
try await withSetupDependencies {
|
||||||
@Dependency(\.cliClient) var cliClient
|
@Dependency(\.cliClient) var cliClient
|
||||||
return try await cliClient[keyPath: keyPath](args, shared())
|
return try await cliClient[keyPath: keyPath](args, shared(command: command))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(
|
func run(
|
||||||
_ keyPath: KeyPath<CliClient, @Sendable (CliClient.SharedOptions) async throws -> String>
|
_ keyPath: KeyPath<CliClient, @Sendable (CliClient.SharedOptions) async throws -> String>,
|
||||||
|
command: String
|
||||||
) async throws {
|
) async throws {
|
||||||
let output = try await runClient(keyPath)
|
let output = try await runClient(keyPath, command: command)
|
||||||
print(output)
|
print(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func run<T>(
|
func run<T>(
|
||||||
_ keyPath: KeyPath<CliClient, @Sendable (T, CliClient.SharedOptions) async throws -> String>,
|
_ keyPath: KeyPath<CliClient, @Sendable (T, CliClient.SharedOptions) async throws -> String>,
|
||||||
|
command: String,
|
||||||
args: T
|
args: T
|
||||||
) async throws {
|
) async throws {
|
||||||
let output = try await runClient(keyPath, args: args)
|
let output = try await runClient(keyPath, command: command, args: args)
|
||||||
print(output)
|
print(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shared() throws -> CliClient.SharedOptions {
|
func shared(command: String) throws -> CliClient.SharedOptions {
|
||||||
try .init(
|
try .init(
|
||||||
allowPreReleaseTag: !configOptions.semvarOptions.preRelease.disablePreRelease,
|
allowPreReleaseTag: !configOptions.semvarOptions.preRelease.disablePreRelease,
|
||||||
dryRun: dryRun,
|
dryRun: dryRun,
|
||||||
gitDirectory: gitDirectory,
|
gitDirectory: gitDirectory,
|
||||||
verbose: verbose,
|
loggingOptions: .init(command: command, verbose: verbose),
|
||||||
target: configOptions.target(),
|
target: configOptions.target(),
|
||||||
branch: .init(includeCommitSha: configOptions.commitSha),
|
branch: .init(includeCommitSha: configOptions.commitSha),
|
||||||
semvar: configOptions.semvarOptions(extraOptions: extraOptions),
|
semvar: configOptions.semvarOptions(extraOptions: extraOptions),
|
||||||
@@ -93,6 +97,8 @@ extension PreReleaseOptions {
|
|||||||
throw ExtraOptionsEmpty()
|
throw ExtraOptionsEmpty()
|
||||||
}
|
}
|
||||||
return .init(prefix: preReleasePrefix, strategy: .command(arguments: extraOptions))
|
return .init(prefix: preReleasePrefix, strategy: .command(arguments: extraOptions))
|
||||||
|
} else if let preReleasePrefix {
|
||||||
|
return .init(prefix: preReleasePrefix, strategy: nil)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,13 +132,12 @@ extension CliClient.SharedOptions {
|
|||||||
gitDirectory: String? = "/baz",
|
gitDirectory: String? = "/baz",
|
||||||
dryRun: Bool = false,
|
dryRun: Bool = false,
|
||||||
target: String = "bar",
|
target: String = "bar",
|
||||||
logLevel: Logger.Level = .trace,
|
|
||||||
versionStrategy: Configuration.VersionStrategy = .semvar(.init())
|
versionStrategy: Configuration.VersionStrategy = .semvar(.init())
|
||||||
) -> Self {
|
) -> Self {
|
||||||
return .init(
|
return .init(
|
||||||
dryRun: dryRun,
|
dryRun: dryRun,
|
||||||
gitDirectory: gitDirectory,
|
gitDirectory: gitDirectory,
|
||||||
logLevel: logLevel,
|
loggingOptions: .init(command: "test", verbose: 2),
|
||||||
target: .init(module: .init(target)),
|
target: .init(module: .init(target)),
|
||||||
branch: versionStrategy.branch,
|
branch: versionStrategy.branch,
|
||||||
semvar: versionStrategy.semvar
|
semvar: versionStrategy.semvar
|
||||||
|
|||||||
Reference in New Issue
Block a user