feat: Begin working on configuration client.
This commit is contained in:
@@ -7,17 +7,41 @@ extension CliVersionCommand {
|
||||
struct Build: AsyncParsableCommand {
|
||||
static let configuration: CommandConfiguration = .init(
|
||||
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.",
|
||||
subcommands: [BranchStyle.self, SemVarStyle.self],
|
||||
defaultSubcommand: SemVarStyle.self
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension CliVersionCommand.Build {
|
||||
struct BranchStyle: AsyncParsableCommand {
|
||||
|
||||
static let configuration: CommandConfiguration = .init(
|
||||
commandName: "branch",
|
||||
abstract: "Build using branch and commit sha as the version.",
|
||||
discussion: "This should generally not be interacted with directly, outside of the plugin usage context."
|
||||
)
|
||||
|
||||
@OptionGroup var globals: GlobalOptions
|
||||
@OptionGroup var globals: GlobalBranchOptions
|
||||
|
||||
func run() async throws {
|
||||
try await globals.run {
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
let output = try await cliClient.build(globals.shared)
|
||||
print(output)
|
||||
}
|
||||
try await globals.shared().run(\.build)
|
||||
}
|
||||
}
|
||||
|
||||
struct SemVarStyle: AsyncParsableCommand {
|
||||
|
||||
static let configuration: CommandConfiguration = .init(
|
||||
commandName: "semvar",
|
||||
abstract: "Generates a version file with SemVar style.",
|
||||
discussion: "This should generally not be interacted with directly, outside of the plugin usage context."
|
||||
)
|
||||
|
||||
@OptionGroup var globals: GlobalSemVarOptions
|
||||
|
||||
func run() async throws {
|
||||
try await globals.shared().run(\.build)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,48 @@ extension CliVersionCommand {
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "bump",
|
||||
abstract: "Bump version of a command-line tool."
|
||||
abstract: "Bump version of a command-line tool.",
|
||||
subcommands: [
|
||||
SemVarStyle.self,
|
||||
BranchStyle.self
|
||||
],
|
||||
defaultSubcommand: SemVarStyle.self
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension CliVersionCommand.Bump {
|
||||
|
||||
struct BranchStyle: AsyncParsableCommand {
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "branch",
|
||||
abstract: "Bump using the current branch and commit sha."
|
||||
)
|
||||
|
||||
@OptionGroup var globals: GlobalOptions
|
||||
@OptionGroup var globals: GlobalBranchOptions
|
||||
|
||||
func run() async throws {
|
||||
try await globals.shared().run(\.bump, args: nil)
|
||||
}
|
||||
}
|
||||
|
||||
struct SemVarStyle: AsyncParsableCommand {
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "semvar",
|
||||
abstract: "Bump using semvar style options."
|
||||
)
|
||||
|
||||
@OptionGroup var globals: GlobalSemVarOptions
|
||||
|
||||
@Flag
|
||||
var bumpOption: CliClient.BumpOption = .patch
|
||||
|
||||
func run() async throws {
|
||||
try await globals.run {
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
let output = try await cliClient.bump(bumpOption, globals.shared)
|
||||
print(output)
|
||||
}
|
||||
try await globals.shared().run(\.bump, args: bumpOption)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ struct CliVersionCommand: AsyncParsableCommand {
|
||||
subcommands: [
|
||||
Build.self,
|
||||
Bump.self,
|
||||
Generate.self,
|
||||
Update.self
|
||||
]
|
||||
Generate.self
|
||||
],
|
||||
defaultSubcommand: Bump.self
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,17 +9,40 @@ extension CliVersionCommand {
|
||||
static let configuration: CommandConfiguration = .init(
|
||||
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.",
|
||||
version: VERSION ?? "0.0.0"
|
||||
subcommands: [BranchStyle.self, SemVarStyle.self],
|
||||
defaultSubcommand: SemVarStyle.self
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension CliVersionCommand.Generate {
|
||||
struct BranchStyle: AsyncParsableCommand {
|
||||
|
||||
static let configuration: CommandConfiguration = .init(
|
||||
commandName: "branch",
|
||||
abstract: "Generates a version file with branch and commit sha as the version.",
|
||||
discussion: "This command can be interacted with directly, outside of the plugin usage context."
|
||||
)
|
||||
|
||||
@OptionGroup var globals: GlobalOptions
|
||||
@OptionGroup var globals: GlobalBranchOptions
|
||||
|
||||
func run() async throws {
|
||||
try await globals.run {
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
let output = try await cliClient.generate(globals.shared)
|
||||
print(output)
|
||||
}
|
||||
try await globals.shared().run(\.generate)
|
||||
}
|
||||
}
|
||||
|
||||
struct SemVarStyle: AsyncParsableCommand {
|
||||
|
||||
static let configuration: CommandConfiguration = .init(
|
||||
commandName: "semvar",
|
||||
abstract: "Generates a version file with SemVar style.",
|
||||
discussion: "This command can be interacted with directly, outside of the plugin usage context."
|
||||
)
|
||||
|
||||
@OptionGroup var globals: GlobalSemVarOptions
|
||||
|
||||
func run() async throws {
|
||||
try await globals.shared().run(\.generate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,39 +2,12 @@ import ArgumentParser
|
||||
@_spi(Internal) import CliClient
|
||||
import Dependencies
|
||||
import Foundation
|
||||
import Rainbow
|
||||
|
||||
func parseTarget(_ target: String) -> URL {
|
||||
let url = URL(fileURLWithPath: target)
|
||||
let urlTest = url
|
||||
.deletingLastPathComponent()
|
||||
struct GlobalOptions<Child: ParsableArguments>: ParsableArguments {
|
||||
|
||||
guard urlTest.lastPathComponent == "Sources" else {
|
||||
return URL(fileURLWithPath: "Sources")
|
||||
.appendingPathComponent(target)
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
extension URL {
|
||||
func fileString() -> String {
|
||||
absoluteString
|
||||
.replacingOccurrences(of: "file://", with: "")
|
||||
}
|
||||
}
|
||||
|
||||
let optionalTemplate = """
|
||||
// Do not set this variable, it is set during the build process.
|
||||
let VERSION: String? = nil
|
||||
|
||||
"""
|
||||
|
||||
let buildTemplate = """
|
||||
// Do not set this variable, it is set during the build process.
|
||||
let VERSION: String = nil
|
||||
|
||||
"""
|
||||
|
||||
struct GlobalOptions: ParsableArguments {
|
||||
@OptionGroup var targetOptions: TargetOptions
|
||||
@OptionGroup var child: Child
|
||||
|
||||
@Option(
|
||||
name: .customLong("git-directory"),
|
||||
@@ -42,19 +15,10 @@ struct GlobalOptions: ParsableArguments {
|
||||
)
|
||||
var gitDirectory: String?
|
||||
|
||||
@Option(
|
||||
name: .shortAndLong,
|
||||
help: "The target for the version file."
|
||||
@Flag(
|
||||
name: .customLong("dry-run"),
|
||||
help: "Print's what would be written to a target version file."
|
||||
)
|
||||
var target: String
|
||||
|
||||
@Option(
|
||||
name: .customLong("filename"),
|
||||
help: "Specify the file name for the version file in the target."
|
||||
)
|
||||
var fileName: String = "Version.swift"
|
||||
|
||||
@Flag(name: .customLong("dry-run"))
|
||||
var dryRun: Bool = false
|
||||
|
||||
@Flag(
|
||||
@@ -62,26 +26,185 @@ struct GlobalOptions: ParsableArguments {
|
||||
help: "Increase logging level, can be passed multiple times (example: -vvv)."
|
||||
)
|
||||
var verbose: Int
|
||||
|
||||
}
|
||||
|
||||
extension GlobalOptions {
|
||||
struct Empty: ParsableArguments {}
|
||||
|
||||
var shared: CliClient.SharedOptions {
|
||||
.init(
|
||||
gitDirectory: gitDirectory,
|
||||
dryRun: dryRun,
|
||||
target: target,
|
||||
logLevel: .init(verbose: verbose)
|
||||
)
|
||||
struct TargetOptions: ParsableArguments {
|
||||
@Option(
|
||||
name: .shortAndLong,
|
||||
help: "Path to the version file, not required if module is set."
|
||||
)
|
||||
var path: String?
|
||||
|
||||
@Option(
|
||||
name: .shortAndLong,
|
||||
help: "The target module name or directory path, not required if path is set."
|
||||
)
|
||||
var module: String?
|
||||
|
||||
@Option(
|
||||
name: [.customShort("n"), .long],
|
||||
help: "The file name inside the target module, required if module is set."
|
||||
)
|
||||
var fileName: String = "Version.swift"
|
||||
|
||||
}
|
||||
|
||||
struct PreReleaseOptions: ParsableArguments {
|
||||
@Flag(
|
||||
name: [.customShort("s"), .customLong("pre-release-branch-style")],
|
||||
help: """
|
||||
Use branch name and commit sha for pre-release suffix, ignored if branch is set.
|
||||
"""
|
||||
)
|
||||
var useBranchAsPreRelease: Bool = false
|
||||
|
||||
@Flag(
|
||||
name: [.customShort("g"), .customLong("pre-release-git-tag-style")],
|
||||
help: """
|
||||
Use `git describe --tags` for pre-release suffix, ignored if branch is set.
|
||||
"""
|
||||
)
|
||||
var useTagAsPreRelease: Bool = false
|
||||
|
||||
@Option(
|
||||
name: .shortAndLong,
|
||||
help: """
|
||||
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\")
|
||||
"""
|
||||
)
|
||||
var custom: String?
|
||||
}
|
||||
|
||||
struct SemVarOptions: ParsableArguments {
|
||||
|
||||
@Flag(
|
||||
name: .long,
|
||||
help: """
|
||||
Fail if an existing version file does not exist, \("ignored if:".yellow.bold) \("branch is set".italic).
|
||||
"""
|
||||
)
|
||||
var requireExistingFile: Bool = false
|
||||
|
||||
@Flag(
|
||||
name: .long,
|
||||
help: "Fail if a sem-var is not parsed from existing file or git tag, used if branch is not set."
|
||||
)
|
||||
var requireExistingSemvar: Bool = false
|
||||
|
||||
@OptionGroup var preRelease: PreReleaseOptions
|
||||
}
|
||||
|
||||
typealias GlobalSemVarOptions = GlobalOptions<SemVarOptions>
|
||||
typealias GlobalBranchOptions = GlobalOptions<Empty>
|
||||
|
||||
extension GlobalSemVarOptions {
|
||||
func shared() throws -> CliClient.SharedOptions {
|
||||
try shared(.semVar(child.semVarOptions()))
|
||||
}
|
||||
}
|
||||
|
||||
func run(_ operation: () async throws -> Void) async throws {
|
||||
extension GlobalBranchOptions {
|
||||
func shared() throws -> CliClient.SharedOptions {
|
||||
try shared(.branchAndCommit)
|
||||
}
|
||||
}
|
||||
|
||||
extension CliClient.SharedOptions {
|
||||
|
||||
func run(_ keyPath: KeyPath<CliClient, @Sendable (Self) async throws -> String>) async throws {
|
||||
try await withDependencies {
|
||||
$0.fileClient = .liveValue
|
||||
$0.gitClient = .liveValue
|
||||
$0.cliClient = .liveValue
|
||||
} operation: {
|
||||
try await operation()
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
let output = try await cliClient[keyPath: keyPath](self)
|
||||
print(output)
|
||||
}
|
||||
}
|
||||
|
||||
func run<T>(
|
||||
_ keyPath: KeyPath<CliClient, @Sendable (T, Self) async throws -> String>,
|
||||
args: T
|
||||
) async throws {
|
||||
try await withDependencies {
|
||||
$0.fileClient = .liveValue
|
||||
$0.gitClient = .liveValue
|
||||
$0.cliClient = .liveValue
|
||||
} operation: {
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
let output = try await cliClient[keyPath: keyPath](args, self)
|
||||
print(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension GlobalOptions {
|
||||
|
||||
func shared(_ versionStrategy: CliClient.VersionStrategy) throws -> CliClient.SharedOptions {
|
||||
try .init(
|
||||
dryRun: dryRun,
|
||||
gitDirectory: gitDirectory,
|
||||
logLevel: .init(verbose: verbose),
|
||||
target: targetOptions.target(),
|
||||
versionStrategy: versionStrategy
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension TargetOptions {
|
||||
func target() throws -> String {
|
||||
guard let path else {
|
||||
guard let module else {
|
||||
print("Neither target path or module was set.")
|
||||
throw InvalidTargetOption()
|
||||
}
|
||||
|
||||
return "\(module)/\(fileName)"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
struct InvalidTargetOption: Error {}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SemVarOptions {
|
||||
func semVarOptions() throws -> CliClient.VersionStrategy.SemVarOptions {
|
||||
try .init(
|
||||
preReleaseStrategy: preRelease.preReleaseStrategy(),
|
||||
requireExistingFile: requireExistingFile,
|
||||
requireExistingSemVar: requireExistingSemvar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import ArgumentParser
|
||||
import CliClient
|
||||
import Dependencies
|
||||
import Foundation
|
||||
import ShellClient
|
||||
|
||||
extension CliVersionCommand {
|
||||
|
||||
struct Update: AsyncParsableCommand {
|
||||
static let configuration: CommandConfiguration = .init(
|
||||
abstract: "Updates a version string to the git tag or git sha.",
|
||||
discussion: "This command can be interacted with directly outside of the plugin context."
|
||||
)
|
||||
|
||||
@OptionGroup var globals: GlobalOptions
|
||||
|
||||
func run() async throws {
|
||||
try await globals.run {
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
let output = try await cliClient.update(globals.shared)
|
||||
print(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum UpdateError: Error {
|
||||
case versionFileDoesNotExist(path: String)
|
||||
}
|
||||
Reference in New Issue
Block a user