feat: Integrates configuration-client.
This commit is contained in:
@@ -2,14 +2,6 @@
|
|||||||
requireExistingFile = true
|
requireExistingFile = true
|
||||||
requireExistingSemVar = true
|
requireExistingSemVar = true
|
||||||
|
|
||||||
[strategy.semvar.preRelease]
|
|
||||||
prefix = 'rc'
|
|
||||||
style = 'custom'
|
|
||||||
usesGitTag = false
|
|
||||||
|
|
||||||
[strategy.semvar.preRelease.branch]
|
|
||||||
includeCommitSha = true
|
|
||||||
|
|
||||||
[target.module]
|
[target.module]
|
||||||
fileName = 'Version.swift'
|
fileName = 'Version.swift'
|
||||||
name = 'cli-version'
|
name = 'cli-version'
|
||||||
@@ -33,66 +33,29 @@ public struct CliClient: Sendable {
|
|||||||
case major, minor, patch, preRelease
|
case major, minor, patch, preRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PreReleaseStrategy: Equatable, Sendable {
|
|
||||||
/// Use output of tag, with branch and commit sha.
|
|
||||||
case branchAndCommit
|
|
||||||
|
|
||||||
/// Provide a custom pre-release tag.
|
|
||||||
indirect case custom(String, PreReleaseStrategy? = nil)
|
|
||||||
|
|
||||||
/// Use the output of `git describe --tags`
|
|
||||||
case tag
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum VersionStrategy: Equatable, Sendable {
|
|
||||||
case branchAndCommit
|
|
||||||
case semVar(SemVarOptions)
|
|
||||||
|
|
||||||
// public typealias SemVarOptions = Configuration.SemVar
|
|
||||||
|
|
||||||
public struct SemVarOptions: Equatable, Sendable {
|
|
||||||
let preReleaseStrategy: PreReleaseStrategy?
|
|
||||||
let requireExistingFile: Bool
|
|
||||||
let requireExistingSemVar: Bool
|
|
||||||
|
|
||||||
public init(
|
|
||||||
preReleaseStrategy: PreReleaseStrategy? = nil,
|
|
||||||
requireExistingFile: Bool = true,
|
|
||||||
requireExistingSemVar: Bool = true
|
|
||||||
) {
|
|
||||||
self.preReleaseStrategy = preReleaseStrategy
|
|
||||||
self.requireExistingFile = requireExistingFile
|
|
||||||
self.requireExistingSemVar = requireExistingSemVar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SharedOptions: Equatable, Sendable {
|
public struct SharedOptions: Equatable, Sendable {
|
||||||
|
|
||||||
let dryRun: Bool
|
let dryRun: Bool
|
||||||
let gitDirectory: String?
|
let gitDirectory: String?
|
||||||
let logLevel: Logger.Level
|
let logLevel: Logger.Level
|
||||||
let preReleaseStrategy: PreReleaseStrategy?
|
let configuration: Configuration
|
||||||
let target: String
|
|
||||||
let versionStrategy: VersionStrategy
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
dryRun: Bool = false,
|
dryRun: Bool = false,
|
||||||
gitDirectory: String? = nil,
|
gitDirectory: String? = nil,
|
||||||
logLevel: Logger.Level = .debug,
|
logLevel: Logger.Level = .debug,
|
||||||
preReleaseStrategy: PreReleaseStrategy? = nil,
|
configuration: Configuration
|
||||||
target: String,
|
|
||||||
versionStrategy: VersionStrategy = .semVar(.init())
|
|
||||||
) {
|
) {
|
||||||
self.dryRun = dryRun
|
self.dryRun = dryRun
|
||||||
self.target = target
|
|
||||||
self.gitDirectory = gitDirectory
|
self.gitDirectory = gitDirectory
|
||||||
self.logLevel = logLevel
|
self.logLevel = logLevel
|
||||||
self.preReleaseStrategy = preReleaseStrategy
|
self.configuration = configuration
|
||||||
self.versionStrategy = versionStrategy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var allowPreReleaseTag: Bool { preReleaseStrategy != nil }
|
var allowPreReleaseTag: Bool {
|
||||||
|
guard let semvar = configuration.strategy?.semvar else { return false }
|
||||||
|
return semvar.preRelease != nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,29 @@ import Dependencies
|
|||||||
import Foundation
|
import Foundation
|
||||||
import GitClient
|
import GitClient
|
||||||
|
|
||||||
|
extension Configuration {
|
||||||
|
func targetUrl(gitDirectory: String?) throws -> URL {
|
||||||
|
guard let target else {
|
||||||
|
throw ConfigurationParsingError.targetNotFound
|
||||||
|
}
|
||||||
|
return try target.url(gitDirectory: gitDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentVersion(targetUrl: URL, gitDirectory: String?) async throws -> CurrentVersionContainer {
|
||||||
|
guard let strategy else {
|
||||||
|
throw ConfigurationParsingError.versionNotFound
|
||||||
|
}
|
||||||
|
return try await strategy.currentVersion(
|
||||||
|
targetUrl: targetUrl,
|
||||||
|
gitDirectory: gitDirectory
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Configuration.Target {
|
extension Configuration.Target {
|
||||||
func url(gitDirectory: String?) throws -> URL {
|
func url(gitDirectory: String?) throws -> URL {
|
||||||
|
@Dependency(\.logger) var logger
|
||||||
|
|
||||||
let filePath: String
|
let filePath: String
|
||||||
|
|
||||||
if let path {
|
if let path {
|
||||||
@@ -15,13 +36,17 @@ extension Configuration.Target {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var path = module.name
|
var path = module.name
|
||||||
if !path.hasPrefix("Sources") || !path.hasPrefix("./Sources") {
|
logger.debug("module.name: \(path)")
|
||||||
path = "Sources/\(path)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.hasPrefix("./") {
|
if path.hasPrefix("./") {
|
||||||
path = String(path.dropFirst(2))
|
path = String(path.dropFirst(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !path.hasPrefix("Sources") {
|
||||||
|
logger.debug("no prefix")
|
||||||
|
path = "Sources/\(path)"
|
||||||
|
}
|
||||||
|
|
||||||
filePath = "\(path)/\(module.fileName)"
|
filePath = "\(path)/\(module.fileName)"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,46 +95,66 @@ extension Configuration.PreReleaseStrategy {
|
|||||||
public extension Configuration.SemVar {
|
public extension Configuration.SemVar {
|
||||||
|
|
||||||
private func applyingPreRelease(_ semVar: SemVar, _ gitDirectory: String?) async throws -> SemVar {
|
private func applyingPreRelease(_ semVar: SemVar, _ gitDirectory: String?) async throws -> SemVar {
|
||||||
guard let preReleaseStrategy = self.preRelease else { return semVar }
|
@Dependency(\.logger) var logger
|
||||||
|
logger.trace("Start apply pre-release to: \(semVar)")
|
||||||
|
guard let preReleaseStrategy = self.preRelease else {
|
||||||
|
logger.trace("No pre-release strategy, returning original 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)")
|
||||||
|
|
||||||
return semVar.applyingPreRelease(preRelease)
|
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 {
|
||||||
@Dependency(\.fileClient) var fileClient
|
@Dependency(\.fileClient) var fileClient
|
||||||
@Dependency(\.gitClient) var gitClient
|
@Dependency(\.gitClient) var gitClient
|
||||||
|
@Dependency(\.logger) var logger
|
||||||
|
|
||||||
let fileOutput = try? await fileClient.semVar(file: file, gitDirectory: gitDirectory)
|
let fileOutput = try? await fileClient.semVar(file: file, gitDirectory: gitDirectory)
|
||||||
var semVar = fileOutput?.semVar
|
var semVar = fileOutput?.semVar
|
||||||
|
|
||||||
|
logger.trace("file output semvar: \(String(describing: semVar))")
|
||||||
|
|
||||||
let usesOptionalType = fileOutput?.usesOptionalType
|
let usesOptionalType = fileOutput?.usesOptionalType
|
||||||
|
|
||||||
if requireExistingFile {
|
// We parsed a semvar from the existing file, use it.
|
||||||
guard let semVar else {
|
if semVar != nil {
|
||||||
throw CliClientError.fileDoesNotExist(path: file.cleanFilePath)
|
|
||||||
}
|
|
||||||
return try await .semVar(
|
return try await .semVar(
|
||||||
applyingPreRelease(semVar, gitDirectory),
|
applyingPreRelease(semVar!, gitDirectory),
|
||||||
usesOptionalType: usesOptionalType ?? false
|
usesOptionalType: usesOptionalType ?? false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Didn't have existing semVar loaded from file, so check for git-tag.
|
if requireExistingFile {
|
||||||
|
logger.debug("Failed to parse existing file, and caller requires it.")
|
||||||
|
throw CliClientError.fileDoesNotExist(path: file.cleanFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace("Does not require existing file, checking git-tag.")
|
||||||
|
|
||||||
|
// Didn't have existing semVar loaded from file, so check for git-tag.
|
||||||
semVar = try await gitClient.version(.init(
|
semVar = try await gitClient.version(.init(
|
||||||
gitDirectory: gitDirectory,
|
gitDirectory: gitDirectory,
|
||||||
style: .tag(exactMatch: false)
|
style: .tag(exactMatch: false)
|
||||||
)).semVar
|
)).semVar
|
||||||
|
|
||||||
if requireExistingSemVar {
|
if semVar != nil {
|
||||||
guard let semVar else {
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
return try await .semVar(
|
return try await .semVar(
|
||||||
applyingPreRelease(semVar, gitDirectory),
|
applyingPreRelease(semVar!, gitDirectory),
|
||||||
usesOptionalType: usesOptionalType ?? false
|
usesOptionalType: usesOptionalType ?? false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if requireExistingSemVar {
|
||||||
|
logger.trace("Caller requires existing semvar and it was not found in file or git-tag.")
|
||||||
|
throw CliClientError.semVarNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// Semvar doesn't exist, so create a new one.
|
||||||
|
logger.trace("Generating new semvar.")
|
||||||
return try await .semVar(
|
return try await .semVar(
|
||||||
applyingPreRelease(.init(), gitDirectory),
|
applyingPreRelease(.init(), gitDirectory),
|
||||||
usesOptionalType: usesOptionalType ?? false
|
usesOptionalType: usesOptionalType ?? false
|
||||||
@@ -143,6 +188,8 @@ extension Configuration.VersionStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ConfigurationParsingError: Error {
|
enum ConfigurationParsingError: Error {
|
||||||
|
case targetNotFound
|
||||||
case pathOrModuleNotSet
|
case pathOrModuleNotSet
|
||||||
case versionStrategyError(message: String)
|
case versionStrategyError(message: String)
|
||||||
|
case versionNotFound
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public extension FileClient {
|
|||||||
guard let versionString else {
|
guard let versionString else {
|
||||||
throw CliClientError.failedToParseVersionFile
|
throw CliClientError.failedToParseVersionFile
|
||||||
}
|
}
|
||||||
|
logger.debug("Parsed version string: \(versionString)")
|
||||||
return (String(versionString), isOptional)
|
return (String(versionString), isOptional)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +42,12 @@ public extension FileClient {
|
|||||||
file: URL,
|
file: URL,
|
||||||
gitDirectory: String?
|
gitDirectory: String?
|
||||||
) async throws -> (semVar: SemVar?, usesOptionalType: Bool) {
|
) async throws -> (semVar: SemVar?, usesOptionalType: Bool) {
|
||||||
|
@Dependency(\.logger) var logger
|
||||||
let (string, usesOptionalType) = try await getVersionString(fileUrl: file, gitDirectory: gitDirectory)
|
let (string, usesOptionalType) = try await getVersionString(fileUrl: file, gitDirectory: gitDirectory)
|
||||||
return (SemVar(string: string), usesOptionalType)
|
let semvar = SemVar(string: string)
|
||||||
|
logger.debug("Semvar: \(String(describing: semvar))")
|
||||||
|
|
||||||
|
return (semvar, usesOptionalType)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,30 +6,6 @@ import GitClient
|
|||||||
@_spi(Internal)
|
@_spi(Internal)
|
||||||
public extension CliClient.SharedOptions {
|
public extension CliClient.SharedOptions {
|
||||||
|
|
||||||
func parseTargetUrl() async throws -> URL {
|
|
||||||
@Dependency(\.fileClient) var fileClient
|
|
||||||
|
|
||||||
let target = target.hasPrefix(".") ? String(target.dropFirst()) : target
|
|
||||||
let targetHasSources = target.hasPrefix("Sources") || target.hasPrefix("/Sources")
|
|
||||||
|
|
||||||
var url = url(for: gitDirectory ?? (targetHasSources ? target : "Sources"))
|
|
||||||
|
|
||||||
if gitDirectory != nil {
|
|
||||||
if !targetHasSources {
|
|
||||||
url.appendPathComponent("Sources")
|
|
||||||
}
|
|
||||||
url.appendPathComponent(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
let isDirectory = try await fileClient.isDirectory(url.cleanFilePath)
|
|
||||||
|
|
||||||
if isDirectory {
|
|
||||||
url.appendPathComponent(Constants.defaultFileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func run(
|
func run(
|
||||||
_ operation: (CurrentVersionContainer) async throws -> Void
|
_ operation: (CurrentVersionContainer) async throws -> Void
|
||||||
@@ -39,12 +15,12 @@ public extension CliClient.SharedOptions {
|
|||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.logger) var logger
|
@Dependency(\.logger) var logger
|
||||||
|
|
||||||
let targetUrl = try await parseTargetUrl()
|
let targetUrl = try configuration.targetUrl(gitDirectory: gitDirectory)
|
||||||
logger.debug("Target: \(targetUrl.cleanFilePath)")
|
logger.debug("Target: \(targetUrl.cleanFilePath)")
|
||||||
|
|
||||||
try await operation(
|
try await operation(
|
||||||
versionStrategy.currentVersion(
|
configuration.currentVersion(
|
||||||
file: targetUrl,
|
targetUrl: targetUrl,
|
||||||
gitDirectory: gitDirectory
|
gitDirectory: gitDirectory
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -124,9 +100,11 @@ extension CliClient.SharedOptions {
|
|||||||
|
|
||||||
switch container.version {
|
switch container.version {
|
||||||
case .string: // When we did not parse a semVar, just write whatever we parsed for the current version.
|
case .string: // When we did not parse a semVar, just write whatever we parsed for the current version.
|
||||||
|
logger.debug("Failed to parse semvar, but got current version string.")
|
||||||
try await write(container)
|
try await write(container)
|
||||||
|
|
||||||
case let .semVar(semVar, usesOptionalType: usesOptionalType):
|
case let .semVar(semVar, usesOptionalType: usesOptionalType):
|
||||||
|
logger.debug("Semvar prior to bumping: \(semVar)")
|
||||||
let bumped = semVar.bump(type, preRelease: nil) // preRelease is already set on semVar.
|
let bumped = semVar.bump(type, preRelease: nil) // preRelease is already set on semVar.
|
||||||
let version = bumped.versionString(withPreReleaseTag: allowPreReleaseTag)
|
let version = bumped.versionString(withPreReleaseTag: allowPreReleaseTag)
|
||||||
logger.debug("Bumped version: \(version)")
|
logger.debug("Bumped version: \(version)")
|
||||||
|
|||||||
@@ -37,16 +37,30 @@ public struct SemVar: CustomStringConvertible, Equatable, Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public init?(string: String) {
|
public init?(string: String) {
|
||||||
|
@Dependency(\.logger) var logger
|
||||||
|
|
||||||
|
logger.trace("Parsing semvar from: \(string)")
|
||||||
|
|
||||||
let parts = string.split(separator: ".")
|
let parts = string.split(separator: ".")
|
||||||
|
logger.trace("parts: \(parts)")
|
||||||
guard parts.count >= 3 else {
|
guard parts.count >= 3 else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let major = Int(String(parts[0].replacingOccurrences(of: "\"", with: "")))
|
|
||||||
let minor = Int(String(parts[1]))
|
|
||||||
|
|
||||||
let patchParts = parts[2].split(separator: "-")
|
let major = Int(String(parts[0].replacingOccurrences(of: "\"", with: "")))
|
||||||
|
logger.trace("major: \(String(describing: major))")
|
||||||
|
|
||||||
|
let minor = Int(String(parts[1]))
|
||||||
|
logger.trace("minor: \(String(describing: minor))")
|
||||||
|
|
||||||
|
let patchParts = parts[2].replacingOccurrences(of: "\"", with: "").split(separator: "-")
|
||||||
|
logger.trace("patchParts: \(String(describing: patchParts))")
|
||||||
|
|
||||||
let patch = Int(patchParts.first ?? "0")
|
let patch = Int(patchParts.first ?? "0")
|
||||||
|
logger.trace("patch: \(String(describing: patch))")
|
||||||
|
|
||||||
let preRelease = String(patchParts.dropFirst().joined(separator: "-"))
|
let preRelease = String(patchParts.dropFirst().joined(separator: "-"))
|
||||||
|
logger.trace("preRelease: \(String(describing: preRelease))")
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
major: major ?? 0,
|
major: major ?? 0,
|
||||||
|
|||||||
@@ -1,104 +1,104 @@
|
|||||||
import ConfigurationClient
|
// import ConfigurationClient
|
||||||
import Dependencies
|
// import Dependencies
|
||||||
import FileClient
|
// import FileClient
|
||||||
import struct Foundation.URL
|
// import struct Foundation.URL
|
||||||
import GitClient
|
// import GitClient
|
||||||
|
//
|
||||||
@_spi(Internal)
|
// @_spi(Internal)
|
||||||
public extension CliClient.PreReleaseStrategy {
|
// public extension CliClient.PreReleaseStrategy {
|
||||||
|
//
|
||||||
func preReleaseString(gitDirectory: String?) async throws -> String {
|
// func preReleaseString(gitDirectory: String?) async throws -> String {
|
||||||
@Dependency(\.gitClient) var gitClient
|
// @Dependency(\.gitClient) var gitClient
|
||||||
switch self {
|
// switch self {
|
||||||
case let .custom(string, child):
|
// case let .custom(string, child):
|
||||||
guard let child else { return string }
|
// guard let child else { return string }
|
||||||
return try await "\(string)-\(child.preReleaseString(gitDirectory: gitDirectory))"
|
// return try await "\(string)-\(child.preReleaseString(gitDirectory: gitDirectory))"
|
||||||
case .tag:
|
// case .tag:
|
||||||
return try await gitClient.version(.init(
|
// return try await gitClient.version(.init(
|
||||||
gitDirectory: gitDirectory,
|
// gitDirectory: gitDirectory,
|
||||||
style: .tag(exactMatch: false)
|
// style: .tag(exactMatch: false)
|
||||||
)).description
|
// )).description
|
||||||
case .branchAndCommit:
|
// case .branchAndCommit:
|
||||||
return try await gitClient.version(.init(
|
// return try await gitClient.version(.init(
|
||||||
gitDirectory: gitDirectory,
|
// gitDirectory: gitDirectory,
|
||||||
style: .branch(commitSha: true)
|
// style: .branch(commitSha: true)
|
||||||
)).description
|
// )).description
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@_spi(Internal)
|
// @_spi(Internal)
|
||||||
public extension CliClient.VersionStrategy.SemVarOptions {
|
// public extension CliClient.VersionStrategy.SemVarOptions {
|
||||||
|
//
|
||||||
private func applyingPreRelease(_ semVar: SemVar, _ gitDirectory: String?) async throws -> SemVar {
|
// private func applyingPreRelease(_ semVar: SemVar, _ gitDirectory: String?) async throws -> SemVar {
|
||||||
guard let preReleaseStrategy else { return semVar }
|
// guard let preReleaseStrategy else { return semVar }
|
||||||
let preRelease = try await preReleaseStrategy.preReleaseString(gitDirectory: gitDirectory)
|
// let preRelease = try await preReleaseStrategy.preReleaseString(gitDirectory: gitDirectory)
|
||||||
return semVar.applyingPreRelease(preRelease)
|
// 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 {
|
||||||
@Dependency(\.fileClient) var fileClient
|
// @Dependency(\.fileClient) var fileClient
|
||||||
@Dependency(\.gitClient) var gitClient
|
// @Dependency(\.gitClient) var gitClient
|
||||||
|
//
|
||||||
let fileOutput = try? await fileClient.semVar(file: file, gitDirectory: gitDirectory)
|
// let fileOutput = try? await fileClient.semVar(file: file, gitDirectory: gitDirectory)
|
||||||
var semVar = fileOutput?.semVar
|
// var semVar = fileOutput?.semVar
|
||||||
let usesOptionalType = fileOutput?.usesOptionalType
|
// let usesOptionalType = fileOutput?.usesOptionalType
|
||||||
|
//
|
||||||
if requireExistingFile {
|
// if requireExistingFile {
|
||||||
guard let semVar else {
|
// guard let semVar else {
|
||||||
throw CliClientError.fileDoesNotExist(path: file.cleanFilePath)
|
// throw CliClientError.fileDoesNotExist(path: file.cleanFilePath)
|
||||||
}
|
// }
|
||||||
return try await .semVar(
|
// return try await .semVar(
|
||||||
applyingPreRelease(semVar, gitDirectory),
|
// applyingPreRelease(semVar, gitDirectory),
|
||||||
usesOptionalType: usesOptionalType ?? false
|
// usesOptionalType: usesOptionalType ?? false
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Didn't have existing semVar loaded from file, so check for git-tag.
|
// // Didn't have existing semVar loaded from file, so check for git-tag.
|
||||||
|
//
|
||||||
semVar = try await gitClient.version(.init(
|
// semVar = try await gitClient.version(.init(
|
||||||
gitDirectory: gitDirectory,
|
// gitDirectory: gitDirectory,
|
||||||
style: .tag(exactMatch: false)
|
// style: .tag(exactMatch: false)
|
||||||
)).semVar
|
// )).semVar
|
||||||
if requireExistingSemVar {
|
// if requireExistingSemVar {
|
||||||
guard let semVar else {
|
// guard let semVar else {
|
||||||
fatalError()
|
// fatalError()
|
||||||
}
|
// }
|
||||||
return try await .semVar(
|
// return try await .semVar(
|
||||||
applyingPreRelease(semVar, gitDirectory),
|
// applyingPreRelease(semVar, gitDirectory),
|
||||||
usesOptionalType: usesOptionalType ?? false
|
// usesOptionalType: usesOptionalType ?? false
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return try await .semVar(
|
// return try await .semVar(
|
||||||
applyingPreRelease(.init(), gitDirectory),
|
// applyingPreRelease(.init(), gitDirectory),
|
||||||
usesOptionalType: usesOptionalType ?? false
|
// usesOptionalType: usesOptionalType ?? false
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@_spi(Internal)
|
// @_spi(Internal)
|
||||||
public extension CliClient.VersionStrategy {
|
// public extension CliClient.VersionStrategy {
|
||||||
|
//
|
||||||
func currentVersion(file: URL, gitDirectory: String?) async throws -> CurrentVersionContainer {
|
// func currentVersion(file: URL, gitDirectory: String?) async throws -> CurrentVersionContainer {
|
||||||
@Dependency(\.gitClient) var gitClient
|
// @Dependency(\.gitClient) var gitClient
|
||||||
|
//
|
||||||
switch self {
|
// switch self {
|
||||||
case .branchAndCommit:
|
// case .branchAndCommit:
|
||||||
return try await .init(
|
// return try await .init(
|
||||||
targetUrl: file,
|
// targetUrl: file,
|
||||||
version: .string(
|
// version: .string(
|
||||||
gitClient.version(.init(
|
// gitClient.version(.init(
|
||||||
gitDirectory: gitDirectory,
|
// gitDirectory: gitDirectory,
|
||||||
style: .branch(commitSha: true)
|
// style: .branch(commitSha: true)
|
||||||
)).description
|
// )).description
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
case let .semVar(options):
|
// case let .semVar(options):
|
||||||
return try await .init(
|
// return try await .init(
|
||||||
targetUrl: file,
|
// targetUrl: file,
|
||||||
version: options.currentVersion(file: file, gitDirectory: gitDirectory)
|
// version: options.currentVersion(file: file, gitDirectory: gitDirectory)
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
61
Sources/cli-version/ConfigurationExtensions.swift
Normal file
61
Sources/cli-version/ConfigurationExtensions.swift
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import ConfigurationClient
|
||||||
|
import Dependencies
|
||||||
|
import FileClient
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Configuration {
|
||||||
|
|
||||||
|
func mergingTarget(_ otherTarget: Configuration.Target?) -> Self {
|
||||||
|
.init(
|
||||||
|
target: otherTarget ?? target,
|
||||||
|
strategy: strategy
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergingStrategy(_ otherStrategy: Configuration.VersionStrategy?) -> Self {
|
||||||
|
.init(
|
||||||
|
target: target,
|
||||||
|
strategy: strategy?.merging(otherStrategy)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Configuration.Branch {
|
||||||
|
func merging(_ other: Self?) -> Self {
|
||||||
|
return .init(includeCommitSha: other?.includeCommitSha ?? includeCommitSha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Configuration.SemVar {
|
||||||
|
func merging(_ other: Self?) -> Self {
|
||||||
|
.init(
|
||||||
|
preRelease: other?.preRelease ?? preRelease,
|
||||||
|
requireExistingFile: other?.requireExistingFile ?? requireExistingFile,
|
||||||
|
requireExistingSemVar: other?.requireExistingSemVar ?? requireExistingSemVar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Configuration.VersionStrategy {
|
||||||
|
func merging(_ other: Self?) -> Self {
|
||||||
|
guard let branch else {
|
||||||
|
guard let semvar else { return self }
|
||||||
|
return .init(semvar: semvar.merging(other?.semvar))
|
||||||
|
}
|
||||||
|
return .init(branch: branch.merging(other?.branch))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func withConfiguration<T>(
|
||||||
|
path: String?,
|
||||||
|
_ operation: (Configuration) async throws -> T
|
||||||
|
) async throws -> T {
|
||||||
|
@Dependency(\.configurationClient) var configurationClient
|
||||||
|
|
||||||
|
let configuration = try await configurationClient.findAndLoad(
|
||||||
|
path != nil ? URL(filePath: path!) : nil
|
||||||
|
)
|
||||||
|
|
||||||
|
return try await operation(configuration)
|
||||||
|
}
|
||||||
@@ -7,6 +7,12 @@ import Rainbow
|
|||||||
|
|
||||||
struct GlobalOptions<Child: ParsableArguments>: ParsableArguments {
|
struct GlobalOptions<Child: ParsableArguments>: ParsableArguments {
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
name: .shortAndLong,
|
||||||
|
help: "Specify the path to a configuration file."
|
||||||
|
)
|
||||||
|
var configurationFile: String?
|
||||||
|
|
||||||
@OptionGroup var targetOptions: TargetOptions
|
@OptionGroup var targetOptions: TargetOptions
|
||||||
@OptionGroup var child: Child
|
@OptionGroup var child: Child
|
||||||
|
|
||||||
@@ -71,7 +77,7 @@ struct PreReleaseOptions: ParsableArguments {
|
|||||||
var useTagAsPreRelease: Bool = false
|
var useTagAsPreRelease: Bool = false
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
name: .shortAndLong,
|
name: .long,
|
||||||
help: """
|
help: """
|
||||||
Apply custom pre-release suffix, can also use branch or tag along with this
|
Apply custom pre-release suffix, can also use branch or tag along with this
|
||||||
option as a prefix, used if branch is not set. (example: \"rc\")
|
option as a prefix, used if branch is not set. (example: \"rc\")
|
||||||
@@ -103,14 +109,18 @@ typealias GlobalSemVarOptions = GlobalOptions<SemVarOptions>
|
|||||||
typealias GlobalBranchOptions = GlobalOptions<Empty>
|
typealias GlobalBranchOptions = GlobalOptions<Empty>
|
||||||
|
|
||||||
extension GlobalSemVarOptions {
|
extension GlobalSemVarOptions {
|
||||||
func shared() throws -> CliClient.SharedOptions {
|
func shared() async throws -> CliClient.SharedOptions {
|
||||||
try shared(.semVar(child.semVarOptions()))
|
try await withConfiguration(path: configurationFile) { configuration in
|
||||||
|
try shared(configuration.mergingStrategy(.init(semvar: child.configSemVarOptions())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GlobalBranchOptions {
|
extension GlobalBranchOptions {
|
||||||
func shared() throws -> CliClient.SharedOptions {
|
func shared() async throws -> CliClient.SharedOptions {
|
||||||
try shared(.branchAndCommit)
|
try await withConfiguration(path: configurationFile) { configuration in
|
||||||
|
try shared(configuration.mergingStrategy(.init(branch: .init())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,13 +156,12 @@ extension CliClient.SharedOptions {
|
|||||||
|
|
||||||
extension GlobalOptions {
|
extension GlobalOptions {
|
||||||
|
|
||||||
func shared(_ versionStrategy: CliClient.VersionStrategy) throws -> CliClient.SharedOptions {
|
func shared(_ configuration: Configuration) throws -> CliClient.SharedOptions {
|
||||||
try .init(
|
.init(
|
||||||
dryRun: dryRun,
|
dryRun: dryRun,
|
||||||
gitDirectory: gitDirectory,
|
gitDirectory: gitDirectory,
|
||||||
logLevel: .init(verbose: verbose),
|
logLevel: .init(verbose: verbose),
|
||||||
target: targetOptions.target(),
|
configuration: configuration
|
||||||
versionStrategy: versionStrategy
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,26 +197,6 @@ private extension TargetOptions {
|
|||||||
|
|
||||||
extension PreReleaseOptions {
|
extension PreReleaseOptions {
|
||||||
|
|
||||||
func preReleaseStrategy() throws -> CliClient.PreReleaseStrategy? {
|
|
||||||
guard let custom else {
|
|
||||||
if useBranchAsPreRelease {
|
|
||||||
return .branchAndCommit
|
|
||||||
} else if useTagAsPreRelease {
|
|
||||||
return .tag
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if useBranchAsPreRelease {
|
|
||||||
return .custom(custom, .branchAndCommit)
|
|
||||||
} else if useTagAsPreRelease {
|
|
||||||
return .custom(custom, .tag)
|
|
||||||
} else {
|
|
||||||
return .custom(custom, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func configPreReleaseStrategy() throws -> Configuration.PreReleaseStrategy? {
|
func configPreReleaseStrategy() throws -> Configuration.PreReleaseStrategy? {
|
||||||
guard let custom else {
|
guard let custom else {
|
||||||
if useBranchAsPreRelease {
|
if useBranchAsPreRelease {
|
||||||
@@ -230,13 +219,6 @@ extension PreReleaseOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension SemVarOptions {
|
extension SemVarOptions {
|
||||||
func semVarOptions() throws -> CliClient.VersionStrategy.SemVarOptions {
|
|
||||||
try .init(
|
|
||||||
preReleaseStrategy: preRelease.preReleaseStrategy(),
|
|
||||||
requireExistingFile: requireExistingFile,
|
|
||||||
requireExistingSemVar: requireExistingSemvar
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func configSemVarOptions() throws -> Configuration.SemVar {
|
func configSemVarOptions() throws -> Configuration.SemVar {
|
||||||
try .init(
|
try .init(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@_spi(Internal) import CliClient
|
@_spi(Internal) import CliClient
|
||||||
|
import ConfigurationClient
|
||||||
import Dependencies
|
import Dependencies
|
||||||
import FileClient
|
import FileClient
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -20,7 +21,7 @@ struct CliClientTests {
|
|||||||
@Dependency(\.cliClient) var client
|
@Dependency(\.cliClient) var client
|
||||||
let output = try await client.build(.testOptions(
|
let output = try await client.build(.testOptions(
|
||||||
target: target,
|
target: target,
|
||||||
versionStrategy: .semVar(.init(requireExistingFile: false))
|
versionStrategy: .init(semvar: .init(requireExistingFile: false))
|
||||||
))
|
))
|
||||||
#expect(output == "/baz/Sources/bar/Version.swift")
|
#expect(output == "/baz/Sources/bar/Version.swift")
|
||||||
}
|
}
|
||||||
@@ -65,7 +66,7 @@ struct CliClientTests {
|
|||||||
@Dependency(\.cliClient) var client
|
@Dependency(\.cliClient) var client
|
||||||
let output = try await client.build(.testOptions(
|
let output = try await client.build(.testOptions(
|
||||||
target: target,
|
target: target,
|
||||||
versionStrategy: .semVar(.init(requireExistingFile: false))
|
versionStrategy: .init(semvar: .init(requireExistingFile: false))
|
||||||
))
|
))
|
||||||
#expect(output == "/baz/Sources/bar/Version.swift")
|
#expect(output == "/baz/Sources/bar/Version.swift")
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ struct CliClientTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum TestArguments {
|
enum TestArguments {
|
||||||
static let testCases = ["bar", "Sources/bar", "/Sources/bar", "./Sources/bar"]
|
static let testCases = ["bar", "Sources/bar", "./Sources/bar"]
|
||||||
static let bumpCases = CliClient.BumpOption.allCases.reduce(into: [(CliClient.BumpOption, Bool)]()) {
|
static let bumpCases = CliClient.BumpOption.allCases.reduce(into: [(CliClient.BumpOption, Bool)]()) {
|
||||||
$0.append(($1, true))
|
$0.append(($1, true))
|
||||||
$0.append(($1, false))
|
$0.append(($1, false))
|
||||||
@@ -130,14 +131,16 @@ extension CliClient.SharedOptions {
|
|||||||
dryRun: Bool = false,
|
dryRun: Bool = false,
|
||||||
target: String = "bar",
|
target: String = "bar",
|
||||||
logLevel: Logger.Level = .trace,
|
logLevel: Logger.Level = .trace,
|
||||||
versionStrategy: CliClient.VersionStrategy = .semVar(.init())
|
versionStrategy: Configuration.VersionStrategy = .init(semvar: .init())
|
||||||
) -> Self {
|
) -> Self {
|
||||||
.init(
|
.init(
|
||||||
dryRun: dryRun,
|
dryRun: dryRun,
|
||||||
gitDirectory: gitDirectory,
|
gitDirectory: gitDirectory,
|
||||||
logLevel: logLevel,
|
logLevel: logLevel,
|
||||||
target: target,
|
configuration: .init(
|
||||||
versionStrategy: versionStrategy
|
target: .init(module: .init(target)),
|
||||||
|
strategy: versionStrategy
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user