feat: Updates configuration, uses json for configuration files and drops toml support.
This commit is contained in:
@@ -1,19 +1,7 @@
|
|||||||
{
|
{
|
||||||
"strategy" : {
|
"strategy" : {
|
||||||
"semvar" : {
|
"branch" : {
|
||||||
"preRelease" : {
|
"includeCommitSha": true
|
||||||
"strategy" : {
|
|
||||||
"command" : {
|
|
||||||
"arguments" : [
|
|
||||||
"git",
|
|
||||||
"describe",
|
|
||||||
"--tags"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"requireExistingFile" : true,
|
|
||||||
"requireExistingSemVar" : true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"target" : {
|
"target" : {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "a6f314a56cd0c1a50e5cace4aacf75ecda58c4cc00e5e13bf7ec110289f943bf",
|
"originHash" : "077fe473b2dff48184d79b6897170a2c87f00a465e6c079889de37e6470001fb",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "combine-schedulers",
|
"identity" : "combine-schedulers",
|
||||||
@@ -118,15 +118,6 @@
|
|||||||
"version" : "600.0.1"
|
"version" : "600.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"identity" : "tomlkit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/LebJe/TOMLKit.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "ec6198d37d495efc6acd4dffbd262cdca7ff9b3f",
|
|
||||||
"version" : "0.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "xctest-dynamic-overlay",
|
"identity" : "xctest-dynamic-overlay",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"),
|
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"),
|
||||||
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"),
|
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"),
|
||||||
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.2"),
|
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.2"),
|
||||||
.package(url: "https://github.com/LebJe/TOMLKit.git", from: "0.5.0"),
|
|
||||||
.package(url: "https://github.com/pointfreeco/swift-custom-dump.git", from: "1.3.3")
|
.package(url: "https://github.com/pointfreeco/swift-custom-dump.git", from: "1.3.3")
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
@@ -50,8 +49,7 @@ let package = Package(
|
|||||||
"FileClient",
|
"FileClient",
|
||||||
.product(name: "CustomDump", package: "swift-custom-dump"),
|
.product(name: "CustomDump", package: "swift-custom-dump"),
|
||||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||||
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
.product(name: "DependenciesMacros", package: "swift-dependencies")
|
||||||
.product(name: "TOMLKit", package: "TOMLKit")
|
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
|
|||||||
@@ -68,14 +68,14 @@ extension GitClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Configuration.PreReleaseStrategy {
|
extension Configuration.PreRelease {
|
||||||
|
|
||||||
func preReleaseString(gitDirectory: String?) async throws -> String {
|
func preReleaseString(gitDirectory: String?) async throws -> String {
|
||||||
@Dependency(\.gitClient) var gitClient
|
@Dependency(\.gitClient) var gitClient
|
||||||
|
|
||||||
let preReleaseString: String
|
let preReleaseString: String
|
||||||
|
|
||||||
if let branch {
|
if let branch = strategy?.branch {
|
||||||
preReleaseString = try await gitClient.version(branch: branch, gitDirectory: gitDirectory)
|
preReleaseString = try await gitClient.version(branch: branch, gitDirectory: gitDirectory)
|
||||||
} else {
|
} else {
|
||||||
preReleaseString = try await gitClient.version(.init(
|
preReleaseString = try await gitClient.version(.init(
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ public extension FileClient {
|
|||||||
let (string, usesOptionalType) = try await getVersionString(fileUrl: file, gitDirectory: gitDirectory)
|
let (string, usesOptionalType) = try await getVersionString(fileUrl: file, gitDirectory: gitDirectory)
|
||||||
let semvar = SemVar(string: string)
|
let semvar = SemVar(string: string)
|
||||||
logger.debug("Semvar: \(String(describing: semvar))")
|
logger.debug("Semvar: \(String(describing: semvar))")
|
||||||
|
|
||||||
return (semvar, usesOptionalType)
|
return (semvar, usesOptionalType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import Dependencies
|
import Dependencies
|
||||||
import DependenciesMacros
|
import DependenciesMacros
|
||||||
import Foundation
|
import Foundation
|
||||||
import TOMLKit
|
|
||||||
|
|
||||||
public extension DependencyValues {
|
public extension DependencyValues {
|
||||||
var coders: Coders {
|
var coders: Coders {
|
||||||
@@ -14,17 +13,13 @@ public extension DependencyValues {
|
|||||||
public struct Coders: Sendable {
|
public struct Coders: Sendable {
|
||||||
public var jsonDecoder: @Sendable () -> JSONDecoder = { .init() }
|
public var jsonDecoder: @Sendable () -> JSONDecoder = { .init() }
|
||||||
public var jsonEncoder: @Sendable () -> JSONEncoder = { .init() }
|
public var jsonEncoder: @Sendable () -> JSONEncoder = { .init() }
|
||||||
public var tomlDecoder: @Sendable () -> TOMLDecoder = { .init() }
|
|
||||||
public var tomlEncoder: @Sendable () -> TOMLEncoder = { .init() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Coders: DependencyKey {
|
extension Coders: DependencyKey {
|
||||||
public static var testValue: Coders {
|
public static var testValue: Coders {
|
||||||
.init(
|
.init(
|
||||||
jsonDecoder: { .init() },
|
jsonDecoder: { .init() },
|
||||||
jsonEncoder: { defaultJsonEncoder },
|
jsonEncoder: { defaultJsonEncoder }
|
||||||
tomlDecoder: { .init() },
|
|
||||||
tomlEncoder: { .init() }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import CustomDump
|
import CustomDump
|
||||||
import Foundation
|
import Foundation
|
||||||
import TOMLKit
|
|
||||||
|
|
||||||
// TODO: Just use json for configuration ??
|
|
||||||
|
|
||||||
/// Represents configuration that can be set via a file, generally in the root of the
|
/// Represents configuration that can be set via a file, generally in the root of the
|
||||||
/// project directory.
|
/// project directory.
|
||||||
@@ -18,7 +15,7 @@ public struct Configuration: Codable, Equatable, Sendable {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
target: Target? = nil,
|
target: Target? = nil,
|
||||||
strategy: VersionStrategy? = .init(semvar: .init())
|
strategy: VersionStrategy? = .semvar(.init())
|
||||||
) {
|
) {
|
||||||
self.target = target
|
self.target = target
|
||||||
self.strategy = strategy
|
self.strategy = strategy
|
||||||
@@ -27,39 +24,26 @@ public struct Configuration: Codable, Equatable, Sendable {
|
|||||||
public static var mock: Self {
|
public static var mock: Self {
|
||||||
.init(
|
.init(
|
||||||
target: .init(module: .init("cli-version")),
|
target: .init(module: .init("cli-version")),
|
||||||
strategy: .init()
|
strategy: .semvar(.init())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var customPreRelease: Self {
|
public static var customPreRelease: Self {
|
||||||
.init(
|
.init(
|
||||||
target: .init(module: .init("cli-version")),
|
target: .init(module: .init("cli-version")),
|
||||||
strategy: .init(semvar: .init(
|
strategy: .semvar(.init(
|
||||||
preRelease: .customBranchPrefix("rc")
|
preRelease: .init(prefix: "rc", strategy: .branch())
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Configuration2: Codable, Equatable, Sendable {
|
|
||||||
public let target: Configuration.Target?
|
|
||||||
public let strategy: Configuration.VersionStrategy2?
|
|
||||||
|
|
||||||
public static let mock = Self(
|
|
||||||
target: .init(module: .init("cli-version")),
|
|
||||||
strategy: .semvar(value: .init(preRelease: .init(
|
|
||||||
strategy: .branch()
|
|
||||||
)))
|
|
||||||
// strategy: .branch()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension Configuration {
|
public extension Configuration {
|
||||||
|
|
||||||
/// Represents a branch version or pre-release strategy.
|
/// Represents a branch version or pre-release strategy.
|
||||||
///
|
///
|
||||||
/// This derives the version from the branch name and short version
|
/// This derives the version or pre-release suffix from the branch name and
|
||||||
/// of the commit sha if configured.
|
/// optionally the short version of the commit sha.
|
||||||
struct Branch: Codable, Equatable, Sendable {
|
struct Branch: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
/// Include the commit sha in the output for this strategy.
|
/// Include the commit sha in the output for this strategy.
|
||||||
@@ -74,14 +58,19 @@ public extension Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PreRelease2: Codable, Equatable, Sendable {
|
/// Represents version strategy for pre-release.
|
||||||
|
///
|
||||||
|
/// This appends a suffix to the version that get's generated from the version strategy.
|
||||||
|
/// For example: `1.0.0-rc-1`
|
||||||
|
///
|
||||||
|
struct PreRelease: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
public let prefix: String?
|
public let prefix: String?
|
||||||
public let strategy: Strategy
|
public let strategy: Strategy?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
prefix: String? = nil,
|
prefix: String? = nil,
|
||||||
strategy: Strategy
|
strategy: Strategy? = nil
|
||||||
) {
|
) {
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.strategy = strategy
|
self.strategy = strategy
|
||||||
@@ -91,100 +80,13 @@ public extension Configuration {
|
|||||||
case branch(includeCommitSha: Bool = true)
|
case branch(includeCommitSha: Bool = true)
|
||||||
case command(arguments: [String])
|
case command(arguments: [String])
|
||||||
case gitTag
|
case gitTag
|
||||||
|
|
||||||
|
public var branch: Branch? {
|
||||||
|
guard case let .branch(includeCommitSha) = self
|
||||||
|
else { return nil }
|
||||||
|
return .init(includeCommitSha: includeCommitSha)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents version strategy for pre-release.
|
|
||||||
///
|
|
||||||
/// This appends a suffix to the version that get's generated from the version strategy.
|
|
||||||
/// For example: `1.0.0-rc-1`
|
|
||||||
///
|
|
||||||
struct PreReleaseStrategy: Codable, Equatable, Sendable, CustomDumpReflectable {
|
|
||||||
|
|
||||||
/// Use branch and commit sha as pre-release suffix.
|
|
||||||
public let branch: Branch?
|
|
||||||
|
|
||||||
/// Use a custom prefix string.
|
|
||||||
public let prefix: String?
|
|
||||||
|
|
||||||
/// An identifier for the type of pre-release.
|
|
||||||
public let style: StyleId
|
|
||||||
|
|
||||||
/// Whether we use `git describe --tags` for part of the suffix, this is only used
|
|
||||||
/// if we have a custom style.
|
|
||||||
public let usesGitTag: Bool?
|
|
||||||
|
|
||||||
init(
|
|
||||||
style: StyleId,
|
|
||||||
branch: Branch? = nil,
|
|
||||||
prefix: String? = nil,
|
|
||||||
usesGitTag: Bool = false
|
|
||||||
) {
|
|
||||||
self.branch = branch
|
|
||||||
self.prefix = prefix
|
|
||||||
self.style = style
|
|
||||||
self.usesGitTag = usesGitTag
|
|
||||||
}
|
|
||||||
|
|
||||||
public var customDumpMirror: Mirror {
|
|
||||||
guard let branch else {
|
|
||||||
return .init(
|
|
||||||
self,
|
|
||||||
children: [
|
|
||||||
"style": style,
|
|
||||||
"prefix": prefix as Any,
|
|
||||||
"usesGitTag": style == .gitTag ? true : (usesGitTag ?? false)
|
|
||||||
],
|
|
||||||
displayStyle: .struct
|
|
||||||
)
|
|
||||||
// return .init(reflecting: self)
|
|
||||||
}
|
|
||||||
return .init(
|
|
||||||
self,
|
|
||||||
children: [
|
|
||||||
"style": style,
|
|
||||||
"branch": branch,
|
|
||||||
"prefix": prefix as Any
|
|
||||||
],
|
|
||||||
displayStyle: .struct
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a pre-release strategy that is derived from calling
|
|
||||||
/// `git describe --tags`.
|
|
||||||
public static let gitTag = Self(style: StyleId.gitTag)
|
|
||||||
|
|
||||||
/// Represents a pre-release strategy that is derived from the branch and commit sha.
|
|
||||||
public static func branch(_ branch: Branch = .init()) -> Self {
|
|
||||||
.init(style: .branch, branch: branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a custom strategy that uses the given value, not deriving any other
|
|
||||||
/// data.
|
|
||||||
public static func custom(_ prefix: String) -> Self {
|
|
||||||
.init(style: .custom, prefix: prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a custom strategy that uses a prefix along with the branch and
|
|
||||||
/// commit sha.
|
|
||||||
public static func customBranchPrefix(
|
|
||||||
_ prefix: String,
|
|
||||||
branch: Branch = .init()
|
|
||||||
) -> Self {
|
|
||||||
.init(style: .custom, branch: branch, prefix: prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a custom strategy that uses a prefix along with the output from
|
|
||||||
/// calling `git describe --tags`.
|
|
||||||
public static func customGitTagPrefix(_ prefix: String) -> Self {
|
|
||||||
.init(style: StyleId.custom, prefix: prefix, usesGitTag: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum StyleId: String, Codable, Sendable {
|
|
||||||
case branch
|
|
||||||
case custom
|
|
||||||
case gitTag
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a semvar version strategy.
|
/// Represents a semvar version strategy.
|
||||||
@@ -194,7 +96,7 @@ public extension Configuration {
|
|||||||
struct SemVar: Codable, Equatable, Sendable {
|
struct SemVar: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
/// Optional pre-releas suffix strategy.
|
/// Optional pre-releas suffix strategy.
|
||||||
public let preRelease: PreReleaseStrategy?
|
public let preRelease: PreRelease?
|
||||||
|
|
||||||
/// Fail if an existing version file does not exist in the target.
|
/// Fail if an existing version file does not exist in the target.
|
||||||
public let requireExistingFile: Bool
|
public let requireExistingFile: Bool
|
||||||
@@ -203,30 +105,7 @@ public extension Configuration {
|
|||||||
public let requireExistingSemVar: Bool
|
public let requireExistingSemVar: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
preRelease: PreReleaseStrategy? = nil,
|
preRelease: PreRelease? = nil,
|
||||||
requireExistingFile: Bool = true,
|
|
||||||
requireExistingSemVar: Bool = true
|
|
||||||
) {
|
|
||||||
self.preRelease = preRelease
|
|
||||||
self.requireExistingFile = requireExistingFile
|
|
||||||
self.requireExistingSemVar = requireExistingSemVar
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SemVar2: Codable, Equatable, Sendable {
|
|
||||||
|
|
||||||
/// Optional pre-releas suffix strategy.
|
|
||||||
public let preRelease: PreRelease2?
|
|
||||||
|
|
||||||
/// Fail if an existing version file does not exist in the target.
|
|
||||||
public let requireExistingFile: Bool
|
|
||||||
|
|
||||||
/// Fail if an existing semvar is not parsed from the file or version generation strategy.
|
|
||||||
public let requireExistingSemVar: Bool
|
|
||||||
|
|
||||||
public init(
|
|
||||||
preRelease: PreRelease2? = nil,
|
|
||||||
requireExistingFile: Bool = true,
|
requireExistingFile: Bool = true,
|
||||||
requireExistingSemVar: Bool = true
|
requireExistingSemVar: Bool = true
|
||||||
) {
|
) {
|
||||||
@@ -313,75 +192,57 @@ public extension Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VersionStrategy2: Codable, Equatable, Sendable {
|
/// Strategy used to generate a version.
|
||||||
|
///
|
||||||
|
/// Typically a `SemVar` strategy or `Branch`.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
enum VersionStrategy: Codable, Equatable, Sendable, CustomDumpReflectable {
|
||||||
case branch(includeCommitSha: Bool = true)
|
case branch(includeCommitSha: Bool = true)
|
||||||
|
|
||||||
case semvar(
|
case semvar(
|
||||||
preRelease: PreRelease2? = nil,
|
preRelease: PreRelease? = nil,
|
||||||
requireExistingFile: Bool? = nil,
|
requireExistingFile: Bool? = nil,
|
||||||
requireExistingSemVar: Bool? = nil
|
requireExistingSemVar: Bool? = nil
|
||||||
)
|
)
|
||||||
|
|
||||||
static func semvar(value: SemVar2) -> Self {
|
public var branch: Branch? {
|
||||||
|
guard case let .branch(includeCommitSha) = self
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
return .init(includeCommitSha: includeCommitSha)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var semvar: SemVar? {
|
||||||
|
guard case let .semvar(preRelease, requireExistingFile, requireExistingSemVar) = self
|
||||||
|
else { return nil }
|
||||||
|
return .init(
|
||||||
|
preRelease: preRelease,
|
||||||
|
requireExistingFile: requireExistingFile ?? false,
|
||||||
|
requireExistingSemVar: requireExistingSemVar ?? false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func branch(_ branch: Branch) -> Self {
|
||||||
|
.branch(includeCommitSha: branch.includeCommitSha)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func semvar(_ value: SemVar) -> Self {
|
||||||
.semvar(
|
.semvar(
|
||||||
preRelease: value.preRelease,
|
preRelease: value.preRelease,
|
||||||
requireExistingFile: value.requireExistingFile,
|
requireExistingFile: value.requireExistingFile,
|
||||||
requireExistingSemVar: value.requireExistingSemVar
|
requireExistingSemVar: value.requireExistingSemVar
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Strategy used to generate a version.
|
|
||||||
///
|
|
||||||
/// Typically a `SemVar` strategy or `Branch`.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
struct VersionStrategy: Codable, Equatable, Sendable, CustomDumpReflectable {
|
|
||||||
|
|
||||||
/// Set if we're using the branch and commit sha to derive the version.
|
|
||||||
public let branch: Branch?
|
|
||||||
|
|
||||||
/// Set if we're using semvar to derive the version.
|
|
||||||
public let semvar: SemVar?
|
|
||||||
|
|
||||||
/// Create a new version strategy that uses branch and commit sha to derive the version.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - branch: The branch strategy options.
|
|
||||||
public init(branch: Branch) {
|
|
||||||
self.branch = branch
|
|
||||||
self.semvar = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new version strategy that uses semvar to derive the version.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - semvar: The semvar strategy options.
|
|
||||||
public init(semvar: SemVar = .init()) {
|
|
||||||
self.branch = nil
|
|
||||||
self.semvar = semvar
|
|
||||||
}
|
|
||||||
|
|
||||||
public var customDumpMirror: Mirror {
|
public var customDumpMirror: Mirror {
|
||||||
if let branch {
|
switch self {
|
||||||
return .init(
|
case .branch:
|
||||||
self,
|
return .init(self, children: ["branch": branch!], displayStyle: .struct)
|
||||||
children: [
|
case .semvar:
|
||||||
"branch": branch
|
return .init(self, children: ["semvar": semvar!], displayStyle: .struct)
|
||||||
],
|
}
|
||||||
displayStyle: .struct
|
|
||||||
)
|
|
||||||
} else if let semvar {
|
|
||||||
return .init(
|
|
||||||
self,
|
|
||||||
children: [
|
|
||||||
"semvar": semvar
|
|
||||||
],
|
|
||||||
displayStyle: .struct
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return .init(reflecting: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,20 +17,20 @@ public extension DependencyValues {
|
|||||||
public struct ConfigurationClient: Sendable {
|
public struct ConfigurationClient: Sendable {
|
||||||
|
|
||||||
/// Find a configuration file in the given directory or in current working directory.
|
/// Find a configuration file in the given directory or in current working directory.
|
||||||
public var find: @Sendable (URL?) async throws -> ConfigurationFile?
|
public var find: @Sendable (URL?) async throws -> URL?
|
||||||
|
|
||||||
/// Load a configuration file.
|
/// Load a configuration file.
|
||||||
public var load: @Sendable (ConfigurationFile) async throws -> Configuration
|
public var load: @Sendable (URL) async throws -> Configuration
|
||||||
|
|
||||||
/// Write a configuration file.
|
/// Write a configuration file.
|
||||||
public var write: @Sendable (Configuration, ConfigurationFile) async throws -> Void
|
public var write: @Sendable (Configuration, URL) async throws -> Void
|
||||||
|
|
||||||
/// Find a configuration file and load it if found.
|
/// Find a configuration file and load it if found.
|
||||||
public func findAndLoad(_ url: URL? = nil) async throws -> Configuration {
|
public func findAndLoad(_ url: URL? = nil) async throws -> Configuration {
|
||||||
guard let url = try await find(url) else {
|
guard let url = try? await find(url) else {
|
||||||
throw ConfigurationClientError.configurationNotFound
|
throw ConfigurationClientError.configurationNotFound
|
||||||
}
|
}
|
||||||
return try await load(url)
|
return (try? await load(url)) ?? .mock
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,49 +40,48 @@ extension ConfigurationClient: DependencyKey {
|
|||||||
public static var liveValue: ConfigurationClient {
|
public static var liveValue: ConfigurationClient {
|
||||||
.init(
|
.init(
|
||||||
find: { try await findConfiguration($0) },
|
find: { try await findConfiguration($0) },
|
||||||
load: { try await $0.load() ?? .mock },
|
load: { try await loadConfiguration($0) },
|
||||||
write: { try await $1.write($0) }
|
write: { try await writeConfiguration($0, to: $1) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func findConfiguration(_ url: URL?) async throws -> ConfigurationFile? {
|
private func findConfiguration(_ url: URL?) async throws -> URL? {
|
||||||
@Dependency(\.fileClient) var fileClient
|
@Dependency(\.fileClient) var fileClient
|
||||||
|
|
||||||
|
let defaultFileName = ConfigurationClient.Constants.defaultFileNameWithoutExtension
|
||||||
|
|
||||||
var url: URL! = url
|
var url: URL! = url
|
||||||
if url == nil {
|
if url == nil {
|
||||||
url = try await URL(filePath: fileClient.currentDirectory())
|
url = try await URL(filePath: fileClient.currentDirectory())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if url is a valid configuration url.
|
if try await fileClient.isDirectory(url.cleanFilePath) {
|
||||||
var configurationFile = ConfigurationFile(url: url)
|
url = url.appending(path: "\(defaultFileName).json")
|
||||||
if let configurationFile, fileClient.fileExists(configurationFile.url) {
|
|
||||||
return configurationFile
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guard try await fileClient.isDirectory(url.cleanFilePath) else {
|
if fileClient.fileExists(url) {
|
||||||
throw ConfigurationClientError.invalidConfigurationDirectory(path: url.cleanFilePath)
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for toml file.
|
|
||||||
let tomlUrl = url.appending(path: "\(ConfigurationClient.Constants.defaultFileNameWithoutExtension).toml")
|
|
||||||
configurationFile = ConfigurationFile(url: tomlUrl)
|
|
||||||
if let configurationFile, fileClient.fileExists(configurationFile.url) {
|
|
||||||
return configurationFile
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for json file.
|
|
||||||
let jsonUrl = url.appending(path: "\(ConfigurationClient.Constants.defaultFileNameWithoutExtension).json")
|
|
||||||
configurationFile = ConfigurationFile(url: jsonUrl)
|
|
||||||
if let configurationFile, fileClient.fileExists(configurationFile.url) {
|
|
||||||
return configurationFile
|
|
||||||
}
|
|
||||||
|
|
||||||
// Couldn't find valid configuration file.
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func loadConfiguration(_ url: URL) async throws -> Configuration {
|
||||||
|
@Dependency(\.coders.jsonDecoder) var jsonDecoder
|
||||||
|
@Dependency(\.fileClient) var fileClient
|
||||||
|
|
||||||
|
let string = try await fileClient.read(url.cleanFilePath)
|
||||||
|
return try jsonDecoder().decode(Configuration.self, from: Data(string.utf8))
|
||||||
|
}
|
||||||
|
|
||||||
enum ConfigurationClientError: Error {
|
enum ConfigurationClientError: Error {
|
||||||
case configurationNotFound
|
case configurationNotFound
|
||||||
case invalidConfigurationDirectory(path: String)
|
case invalidConfigurationDirectory(path: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func writeConfiguration(_ configuration: Configuration, to url: URL) async throws {
|
||||||
|
@Dependency(\.fileClient) var fileClient
|
||||||
|
@Dependency(\.coders.jsonEncoder) var jsonEncoder
|
||||||
|
let data = try jsonEncoder().encode(configuration)
|
||||||
|
try await fileClient.write(data, url)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import Dependencies
|
|
||||||
import FileClient
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Represents a configuration file type and location.
|
|
||||||
public enum ConfigurationFile: Equatable, Sendable {
|
|
||||||
|
|
||||||
/// A json configuration file.
|
|
||||||
case json(URL)
|
|
||||||
|
|
||||||
/// A toml configuration file.
|
|
||||||
case toml(URL)
|
|
||||||
|
|
||||||
/// Default configuration file, which is a toml file
|
|
||||||
/// with the name of '.bump-version.toml'
|
|
||||||
public static var `default`: Self {
|
|
||||||
.toml(URL(
|
|
||||||
filePath: "\(ConfigurationClient.Constants.defaultFileNameWithoutExtension).toml"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new file location from the given url.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - url: The url for the file.
|
|
||||||
public init?(url: URL) {
|
|
||||||
if url.pathExtension == "toml" {
|
|
||||||
self = .toml(url)
|
|
||||||
} else if url.pathExtension == "json" {
|
|
||||||
self = .json(url)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The url of the file.
|
|
||||||
public var url: URL {
|
|
||||||
switch self {
|
|
||||||
case let .json(url): return url
|
|
||||||
case let .toml(url): return url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConfigurationFile {
|
|
||||||
|
|
||||||
func load() async throws -> Configuration? {
|
|
||||||
@Dependency(\.coders) var coders
|
|
||||||
@Dependency(\.fileClient) var fileClient
|
|
||||||
|
|
||||||
switch self {
|
|
||||||
case .json:
|
|
||||||
let data = try await Data(fileClient.read(url.cleanFilePath).utf8)
|
|
||||||
return try? coders.jsonDecoder().decode(Configuration.self, from: data)
|
|
||||||
case .toml:
|
|
||||||
let string = try await fileClient.read(url.cleanFilePath)
|
|
||||||
return try? coders.tomlDecoder().decode(Configuration.self, from: string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func write(_ configuration: Configuration) async throws {
|
|
||||||
@Dependency(\.coders) var coders
|
|
||||||
@Dependency(\.fileClient) var fileClient
|
|
||||||
|
|
||||||
let data: Data
|
|
||||||
|
|
||||||
switch self {
|
|
||||||
case .json:
|
|
||||||
data = try coders.jsonEncoder().encode(configuration)
|
|
||||||
case .toml:
|
|
||||||
data = try Data(coders.tomlEncoder().encode(configuration).utf8)
|
|
||||||
}
|
|
||||||
|
|
||||||
try await fileClient.write(data, url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -40,9 +40,9 @@ extension Configuration.VersionStrategy {
|
|||||||
func merging(_ other: Self?) -> Self {
|
func merging(_ other: Self?) -> Self {
|
||||||
guard let branch else {
|
guard let branch else {
|
||||||
guard let semvar else { return self }
|
guard let semvar else { return self }
|
||||||
return .init(semvar: semvar.merging(other?.semvar))
|
return .semvar(semvar.merging(other?.semvar))
|
||||||
}
|
}
|
||||||
return .init(branch: branch.merging(other?.branch))
|
return .branch(branch.merging(other?.branch))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ typealias GlobalBranchOptions = GlobalOptions<Empty>
|
|||||||
extension GlobalSemVarOptions {
|
extension GlobalSemVarOptions {
|
||||||
func shared() async throws -> CliClient.SharedOptions {
|
func shared() async throws -> CliClient.SharedOptions {
|
||||||
try await withConfiguration(path: configurationFile) { configuration in
|
try await withConfiguration(path: configurationFile) { configuration in
|
||||||
try shared(configuration.mergingStrategy(.init(semvar: child.configSemVarOptions())))
|
try shared(configuration.mergingStrategy(.semvar(child.configSemVarOptions())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ extension GlobalSemVarOptions {
|
|||||||
extension GlobalBranchOptions {
|
extension GlobalBranchOptions {
|
||||||
func shared() async throws -> CliClient.SharedOptions {
|
func shared() async throws -> CliClient.SharedOptions {
|
||||||
try await withConfiguration(path: configurationFile) { configuration in
|
try await withConfiguration(path: configurationFile) { configuration in
|
||||||
try shared(configuration.mergingStrategy(.init(branch: .init())))
|
try shared(configuration.mergingStrategy(.branch()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +197,26 @@ private extension TargetOptions {
|
|||||||
|
|
||||||
extension PreReleaseOptions {
|
extension PreReleaseOptions {
|
||||||
|
|
||||||
func configPreReleaseStrategy() throws -> Configuration.PreReleaseStrategy? {
|
// FIX:
|
||||||
guard let custom else {
|
func configPreReleaseStrategy() throws -> Configuration.PreRelease? {
|
||||||
if useBranchAsPreRelease {
|
|
||||||
return .branch()
|
|
||||||
} else if useTagAsPreRelease {
|
|
||||||
return .gitTag
|
|
||||||
} else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
// guard let custom else {
|
||||||
}
|
// if useBranchAsPreRelease {
|
||||||
|
// return .branch()
|
||||||
if useBranchAsPreRelease {
|
// } else if useTagAsPreRelease {
|
||||||
return .customBranchPrefix(custom)
|
// return .gitTag
|
||||||
} else if useTagAsPreRelease {
|
// } else {
|
||||||
return .customGitTagPrefix(custom)
|
// return nil
|
||||||
} else {
|
// }
|
||||||
return .custom(custom)
|
// }
|
||||||
}
|
//
|
||||||
|
// if useBranchAsPreRelease {
|
||||||
|
// return .customBranchPrefix(custom)
|
||||||
|
// } else if useTagAsPreRelease {
|
||||||
|
// return .customGitTagPrefix(custom)
|
||||||
|
// } else {
|
||||||
|
// return .custom(custom)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,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: .init(semvar: .init(requireExistingFile: false))
|
versionStrategy: .semvar(requireExistingFile: false)
|
||||||
))
|
))
|
||||||
#expect(output == "/baz/Sources/bar/Version.swift")
|
#expect(output == "/baz/Sources/bar/Version.swift")
|
||||||
}
|
}
|
||||||
@@ -66,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: .init(semvar: .init(requireExistingFile: false))
|
versionStrategy: .semvar(requireExistingFile: false)
|
||||||
))
|
))
|
||||||
#expect(output == "/baz/Sources/bar/Version.swift")
|
#expect(output == "/baz/Sources/bar/Version.swift")
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ extension CliClient.SharedOptions {
|
|||||||
dryRun: Bool = false,
|
dryRun: Bool = false,
|
||||||
target: String = "bar",
|
target: String = "bar",
|
||||||
logLevel: Logger.Level = .trace,
|
logLevel: Logger.Level = .trace,
|
||||||
versionStrategy: Configuration.VersionStrategy = .init(semvar: .init())
|
versionStrategy: Configuration.VersionStrategy = .semvar(.init())
|
||||||
) -> Self {
|
) -> Self {
|
||||||
.init(
|
.init(
|
||||||
dryRun: dryRun,
|
dryRun: dryRun,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Dependencies
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Testing
|
import Testing
|
||||||
import TestSupport
|
import TestSupport
|
||||||
import TOMLKit
|
|
||||||
|
|
||||||
@Suite("ConfigurationClientTests")
|
@Suite("ConfigurationClientTests")
|
||||||
struct ConfigurationClientTests {
|
struct ConfigurationClientTests {
|
||||||
@@ -19,28 +18,6 @@ struct ConfigurationClientTests {
|
|||||||
let decoded = try coders.jsonDecoder().decode(Configuration.self, from: encoded)
|
let decoded = try coders.jsonDecoder().decode(Configuration.self, from: encoded)
|
||||||
|
|
||||||
#expect(decoded == configuration)
|
#expect(decoded == configuration)
|
||||||
|
|
||||||
let tomlEncoded = try coders.tomlEncoder().encode(configuration)
|
|
||||||
let tomlDecoded = try coders.tomlDecoder().decode(
|
|
||||||
Configuration.self,
|
|
||||||
from: tomlEncoded
|
|
||||||
)
|
|
||||||
#expect(tomlDecoded == configuration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(arguments: ["foo", ".foo"])
|
|
||||||
func configurationFile(fileName: String) {
|
|
||||||
for ext in ["toml", "json", "bar"] {
|
|
||||||
let file = ConfigurationFile(url: URL(filePath: "\(fileName).\(ext)"))
|
|
||||||
switch ext {
|
|
||||||
case "toml":
|
|
||||||
#expect(file == .toml(URL(filePath: "\(fileName).toml")))
|
|
||||||
case "json":
|
|
||||||
#expect(file == .json(URL(filePath: "\(fileName).json")))
|
|
||||||
default:
|
|
||||||
#expect(file == nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,16 +27,15 @@ struct ConfigurationClientTests {
|
|||||||
try await run {
|
try await run {
|
||||||
@Dependency(\.configurationClient) var configurationClient
|
@Dependency(\.configurationClient) var configurationClient
|
||||||
|
|
||||||
for ext in ["toml", "json"] {
|
for ext in ["json"] {
|
||||||
let fileUrl = url.appending(path: "test.\(ext)")
|
let fileUrl = url.appending(path: "test.\(ext)")
|
||||||
let configuration = Configuration.mock
|
let configuration = Configuration.mock
|
||||||
let configurationFile = ConfigurationFile(url: fileUrl)!
|
|
||||||
|
|
||||||
try await configurationClient.write(configuration, configurationFile)
|
try await configurationClient.write(configuration, fileUrl)
|
||||||
let loaded = try await configurationClient.load(configurationFile)
|
let loaded = try await configurationClient.load(fileUrl)
|
||||||
#expect(loaded == configuration)
|
#expect(loaded == configuration)
|
||||||
|
|
||||||
let findAndLoaded = try await configurationClient.findAndLoad(configurationFile.url)
|
let findAndLoaded = try await configurationClient.findAndLoad(fileUrl)
|
||||||
#expect(findAndLoaded == configuration)
|
#expect(findAndLoaded == configuration)
|
||||||
|
|
||||||
try FileManager.default.removeItem(at: fileUrl)
|
try FileManager.default.removeItem(at: fileUrl)
|
||||||
@@ -84,13 +60,12 @@ struct ConfigurationClientTests {
|
|||||||
#expect(Bool(true))
|
#expect(Bool(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
for ext in ["toml", "json"] {
|
for ext in ["json"] {
|
||||||
let fileUrl = url.appending(path: ".bump-version.\(ext)")
|
let fileUrl = url.appending(path: ".bump-version.\(ext)")
|
||||||
let configuration = Configuration.mock
|
let configuration = Configuration.mock
|
||||||
let configurationFile = ConfigurationFile(url: fileUrl)!
|
|
||||||
|
|
||||||
try await configurationClient.write(configuration, configurationFile)
|
try await configurationClient.write(configuration, fileUrl)
|
||||||
let loaded = try await configurationClient.findAndLoad(url)
|
let loaded = try await configurationClient.findAndLoad(fileUrl)
|
||||||
#expect(loaded == configuration)
|
#expect(loaded == configuration)
|
||||||
|
|
||||||
try FileManager.default.removeItem(at: fileUrl)
|
try FileManager.default.removeItem(at: fileUrl)
|
||||||
|
|||||||
Reference in New Issue
Block a user