feat: Renaming to bump-version
All checks were successful
CI / Ubuntu (push) Successful in 2m55s

This commit is contained in:
2024-12-24 16:39:38 -05:00
parent 9ea0ab566e
commit 322327d6d7
12 changed files with 6 additions and 6 deletions

View File

@@ -0,0 +1,17 @@
import ArgumentParser
import Foundation
@main
struct Application: AsyncParsableCommand {
static let configuration: CommandConfiguration = .init(
commandName: "bump-version",
version: VERSION ?? "0.0.0",
subcommands: [
BuildCommand.self,
BumpCommand.self,
GenerateCommand.self,
UtilsCommand.self
],
defaultSubcommand: BumpCommand.self
)
}

View File

@@ -0,0 +1,19 @@
import ArgumentParser
import CliClient
import Foundation
import ShellClient
struct BuildCommand: AsyncParsableCommand {
static let configuration: CommandConfiguration = .init(
commandName: "build",
abstract: "Used for the build with version plugin.",
discussion: "This should generally not be interacted with directly, outside of the build plugin.",
shouldDisplay: false
)
@OptionGroup var globals: GlobalOptions
func run() async throws {
try await globals.run(\.build)
}
}

View File

@@ -0,0 +1,26 @@
import ArgumentParser
import CliClient
import Dependencies
struct BumpCommand: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "bump",
abstract: "Bump version of a command-line tool."
)
@OptionGroup var globals: GlobalOptions
@Flag(
help: """
The semvar bump option, this is ignored if the configuration is set to use a branch/commit sha strategy.
"""
)
var bumpOption: CliClient.BumpOption = .patch
func run() async throws {
try await globals.run(\.bump, args: bumpOption)
}
}
extension CliClient.BumpOption: EnumerableFlag {}

View File

@@ -0,0 +1,19 @@
import ArgumentParser
import CliClient
import Dependencies
import Foundation
import ShellClient
struct GenerateCommand: AsyncParsableCommand {
static let configuration: CommandConfiguration = .init(
commandName: "generate",
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."
)
@OptionGroup var globals: GlobalOptions
func run() async throws {
try await globals.run(\.generate)
}
}

View File

@@ -0,0 +1,95 @@
import ArgumentParser
import ConfigurationClient
import CustomDump
import Dependencies
import FileClient
import Foundation
struct UtilsCommand: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "utils",
abstract: "Utility commands",
subcommands: [
DumpConfig.self,
GenerateConfig.self
]
)
}
extension UtilsCommand {
struct DumpConfig: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "dump-config",
abstract: "Show the parsed configuration.",
aliases: ["dc"]
)
@OptionGroup var globals: GlobalOptions
func run() async throws {
let configuration = try await globals.runClient(\.parsedConfiguration)
customDump(configuration)
}
}
struct GenerateConfig: AsyncParsableCommand {
static let configuration: CommandConfiguration = .init(
commandName: "generate-config",
abstract: "Generate a configuration file.",
aliases: ["gc"]
)
@OptionGroup var configOptions: ConfigurationOptions
@Flag(
help: "The style of the configuration."
)
var style: Style = .semvar
@Argument(
help: """
Arguments / options used for custom pre-release, options / flags must proceed a '--' in
the command. These are ignored if the `--custom` flag is not set.
"""
)
var extraOptions: [String] = []
func run() async throws {
try await withSetupDependencies {
@Dependency(\.configurationClient) var configurationClient
let strategy: Configuration.VersionStrategy
switch style {
case .branch:
strategy = .branch(includeCommitSha: configOptions.commitSha)
case .semvar:
strategy = try .semvar(configOptions.semvarOptions(extraOptions: extraOptions))
}
let configuration = try Configuration(
target: configOptions.target(),
strategy: strategy
)
let url: URL
switch configOptions.configurationFile {
case let .some(path):
url = URL(filePath: path)
case .none:
url = URL(filePath: ".bump-version.json")
}
try await configurationClient.write(configuration, url)
print(url.cleanFilePath)
}
}
}
}
extension UtilsCommand.GenerateConfig {
enum Style: EnumerableFlag {
case branch, semvar
}
}

View File

@@ -0,0 +1,5 @@
# Application
## Articles
- <doc:Installation>

View File

@@ -0,0 +1,3 @@
# Installation
You can install the command-line application by...

View File

@@ -0,0 +1,144 @@
import ArgumentParser
@_spi(Internal) import CliClient
import ConfigurationClient
import Dependencies
import Foundation
import Rainbow
struct GlobalOptions: ParsableArguments {
@OptionGroup
var configOptions: ConfigurationOptions
@Option(
name: .customLong("git-directory"),
help: "The git directory for the version (default: current directory)"
)
var gitDirectory: String?
@Flag(
name: .customLong("dry-run"),
help: "Print's what would be written to a target version file."
)
var dryRun: Bool = false
@Flag(
name: .shortAndLong,
help: "Increase logging level, can be passed multiple times (example: -vvv)."
)
var verbose: Int
@Argument(
help: """
Arguments / options used for custom pre-release, options / flags must proceed a '--' in
the command. These are ignored if the `--custom` flag is not set.
"""
)
var extraOptions: [String] = []
}
struct ConfigurationOptions: ParsableArguments {
@Option(
name: .shortAndLong,
help: "Specify the path to a configuration file.",
completion: .file(extensions: ["json"])
)
var configurationFile: String?
@OptionGroup var targetOptions: TargetOptions
@OptionGroup var semvarOptions: SemVarOptions
@Flag(
name: .long,
inversion: .prefixedNo,
help: """
Include the short commit sha in version or pre-release branch style output.
"""
)
var commitSha: Bool = true
}
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: .shortAndLong,
help: ""
)
var disablePreRelease: Bool = false
@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: .long,
help: """
Add / use a pre-release prefix string.
"""
)
var preReleasePrefix: String?
@Flag(
name: .long,
help: """
Apply custom pre-release suffix, using extra options / arguments passed in after a '--'.
"""
)
var customPreRelease: Bool = false
}
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
}

View File

@@ -0,0 +1,123 @@
import CliClient
import ConfigurationClient
import Dependencies
import FileClient
import GitClient
@discardableResult
func withSetupDependencies<T>(
_ operation: () async throws -> T
) async throws -> T {
try await withDependencies {
$0.fileClient = .liveValue
$0.gitClient = .liveValue
$0.cliClient = .liveValue
$0.configurationClient = .liveValue
} operation: {
try await operation()
}
}
extension GlobalOptions {
func runClient<T>(
_ keyPath: KeyPath<CliClient, @Sendable (CliClient.SharedOptions) async throws -> T>
) async throws -> T {
try await withSetupDependencies {
@Dependency(\.cliClient) var cliClient
return try await cliClient[keyPath: keyPath](shared())
}
}
func runClient<A, T>(
_ keyPath: KeyPath<CliClient, @Sendable (A, CliClient.SharedOptions) async throws -> T>,
args: A
) async throws -> T {
try await withSetupDependencies {
@Dependency(\.cliClient) var cliClient
return try await cliClient[keyPath: keyPath](args, shared())
}
}
func run(
_ keyPath: KeyPath<CliClient, @Sendable (CliClient.SharedOptions) async throws -> String>
) async throws {
let output = try await runClient(keyPath)
print(output)
}
func run<T>(
_ keyPath: KeyPath<CliClient, @Sendable (T, CliClient.SharedOptions) async throws -> String>,
args: T
) async throws {
let output = try await runClient(keyPath, args: args)
print(output)
}
func shared() throws -> CliClient.SharedOptions {
try .init(
allowPreReleaseTag: !configOptions.semvarOptions.preRelease.disablePreRelease,
dryRun: dryRun,
gitDirectory: gitDirectory,
verbose: verbose,
target: configOptions.target(),
branch: .init(includeCommitSha: configOptions.commitSha),
semvar: configOptions.semvarOptions(extraOptions: extraOptions),
configurationFile: configOptions.configurationFile
)
}
}
private extension TargetOptions {
func configTarget() throws -> Configuration.Target? {
guard let path else {
guard let module else {
return nil
}
return .init(module: .init(module, fileName: fileName))
}
return .init(path: path)
}
}
extension PreReleaseOptions {
func configPreReleaseStrategy(includeCommitSha: Bool, extraOptions: [String]) throws -> Configuration.PreRelease? {
if useBranchAsPreRelease {
return .init(prefix: preReleasePrefix, strategy: .branch(includeCommitSha: includeCommitSha))
} else if useTagAsPreRelease {
return .init(prefix: preReleasePrefix, strategy: .gitTag)
} else if customPreRelease {
guard extraOptions.count > 0 else {
throw ExtraOptionsEmpty()
}
return .init(prefix: preReleasePrefix, strategy: .command(arguments: extraOptions))
}
return nil
}
}
extension SemVarOptions {
func configSemVarOptions(includeCommitSha: Bool, extraOptions: [String]) throws -> Configuration.SemVar {
try .init(
preRelease: preRelease.configPreReleaseStrategy(includeCommitSha: includeCommitSha, extraOptions: extraOptions),
requireExistingFile: requireExistingFile,
requireExistingSemVar: requireExistingSemvar
)
}
}
extension ConfigurationOptions {
func target() throws -> Configuration.Target? {
try targetOptions.configTarget()
}
func semvarOptions(extraOptions: [String]) throws -> Configuration.SemVar {
try semvarOptions.configSemVarOptions(includeCommitSha: commitSha, extraOptions: extraOptions)
}
}
struct ExtraOptionsEmpty: Error {}

View File

@@ -0,0 +1,2 @@
// Do not set this variable, it is set during the build process.
let VERSION: String? = "0.1.1"