feat: Beginning to refactor cli-client
This commit is contained in:
@@ -149,31 +149,6 @@ public extension ConfigurationClient {
|
||||
}
|
||||
}
|
||||
|
||||
// @_spi(Internal)
|
||||
// public extension Configuration {
|
||||
|
||||
// func defaultPlaybookDirectory() throws -> String {
|
||||
// let playbookDirectory = Bundle.module.url(
|
||||
// forResource: Constants.playbookBundleDirectoryName,
|
||||
// withExtension: nil
|
||||
// )
|
||||
// guard let playbookDirectory else {
|
||||
// throw CliClientError.playbookDirectoryNotFound
|
||||
// }
|
||||
// return playbookDirectory.cleanFilePath
|
||||
// }
|
||||
|
||||
// func ensuredPlaybookDirectory(_ optionalDirectory: String?) throws -> String {
|
||||
// guard let directory = optionalDirectory else {
|
||||
// guard let directory = playbook?.directory else {
|
||||
// return try defaultPlaybookDirectory()
|
||||
// }
|
||||
// return directory
|
||||
// }
|
||||
// return directory
|
||||
// }
|
||||
// }
|
||||
|
||||
@_spi(Internal)
|
||||
public extension Optional where Wrapped == String {
|
||||
var orDefault: ShellCommand.Shell {
|
||||
|
||||
216
Sources/CliClient/CliClient+RunPlaybook.swift
Normal file
216
Sources/CliClient/CliClient+RunPlaybook.swift
Normal file
@@ -0,0 +1,216 @@
|
||||
import ConfigurationClient
|
||||
import Dependencies
|
||||
import Foundation
|
||||
import PlaybookClient
|
||||
|
||||
extension CliClient.RunPlaybook {
|
||||
|
||||
static func makeCommonArguments(
|
||||
configuration: Configuration,
|
||||
inventoryFilePath: String?
|
||||
) async throws -> [String] {
|
||||
@Dependency(\.logger) var logger
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
|
||||
let playbookDirectory = try await playbookClient.playbookDirectory(configuration)
|
||||
let playbookPath = "\(playbookDirectory)/\(Constants.playbookFileName)"
|
||||
logger.trace("Playbook path: \(playbookPath)")
|
||||
|
||||
let inventoryPath = ensuredInventoryPath(
|
||||
inventoryFilePath,
|
||||
configuration: configuration,
|
||||
playbookDirectory: playbookDirectory
|
||||
)
|
||||
logger.trace("Inventory path: \(inventoryPath)")
|
||||
|
||||
var arguments = [
|
||||
Constants.playbookCommand, playbookPath,
|
||||
"--inventory", inventoryPath
|
||||
]
|
||||
|
||||
if let defaultArgs = configuration.args {
|
||||
arguments.append(contentsOf: defaultArgs)
|
||||
}
|
||||
|
||||
if configuration.useVaultArgs, let vaultArgs = configuration.vault.args {
|
||||
arguments.append(contentsOf: vaultArgs)
|
||||
}
|
||||
|
||||
logger.trace("Common arguments: \(inventoryPath)")
|
||||
return arguments
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension CliClient.RunPlaybook.BuildOptions {
|
||||
private func applyArguments(
|
||||
to arguments: inout [String],
|
||||
configuration: Configuration
|
||||
) throws {
|
||||
let projectDirectory = projectDirectory
|
||||
?? ProcessInfo.processInfo.environment["PWD"]
|
||||
|
||||
guard let projectDirectory else {
|
||||
throw CliClientError.projectDirectoryNotFound
|
||||
}
|
||||
|
||||
arguments.append(contentsOf: [
|
||||
"--tags", "build-project",
|
||||
"--extra-vars", "project_dir=\(projectDirectory)"
|
||||
])
|
||||
|
||||
if let extraOptions = extraOptions {
|
||||
arguments.append(contentsOf: extraOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CliClient.RunPlaybook.CreateOptions {
|
||||
private func applyArguments(
|
||||
to arguments: inout [String],
|
||||
configuration: Configuration
|
||||
) throws {
|
||||
let json = try createJSONData(configuration: configuration)
|
||||
|
||||
arguments.append(contentsOf: [
|
||||
"--tags", "setup-project",
|
||||
"--extra-vars", "project_dir=\(projectDirectory)",
|
||||
"--extra-vars", "'\(json)'"
|
||||
])
|
||||
|
||||
if let extraOptions {
|
||||
arguments.append(contentsOf: extraOptions)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension CliClient.PlaybookOptions.Route {
|
||||
|
||||
private func parseInventoryPath(
|
||||
_ configuration: Configuration,
|
||||
_ playbookDirectory: String
|
||||
) -> String {
|
||||
let inventoryFilePath: String?
|
||||
|
||||
switch self {
|
||||
case let .build(options):
|
||||
inventoryFilePath = options.inventoryFilePath
|
||||
case let .create(options):
|
||||
inventoryFilePath = options.inventoryFilePath
|
||||
}
|
||||
|
||||
return ensuredInventoryPath(
|
||||
inventoryFilePath,
|
||||
configuration: configuration,
|
||||
playbookDirectory: playbookDirectory
|
||||
)
|
||||
}
|
||||
|
||||
func makeArguments(configuration: Configuration) async throws -> [String] {
|
||||
@Dependency(\.logger) var logger
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
|
||||
let playbookDirectory = try await playbookClient.playbookDirectory(configuration)
|
||||
let playbookPath = "\(playbookDirectory)/\(Constants.playbookFileName)"
|
||||
logger.trace("Playbook path: \(playbookPath)")
|
||||
|
||||
let inventoryPath = parseInventoryPath(configuration, playbookDirectory)
|
||||
logger.trace("Inventory path: \(inventoryPath)")
|
||||
|
||||
var arguments = [
|
||||
Constants.playbookCommand, playbookPath,
|
||||
"--inventory", inventoryPath
|
||||
]
|
||||
|
||||
if let defaultArgs = configuration.args {
|
||||
arguments.append(contentsOf: defaultArgs)
|
||||
}
|
||||
|
||||
if configuration.useVaultArgs, let vaultArgs = configuration.vault.args {
|
||||
arguments.append(contentsOf: vaultArgs)
|
||||
}
|
||||
|
||||
// try applyArguments(to: &arguments, configuration: configuration)
|
||||
|
||||
return arguments
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: We're not using the `Coders` client because we generally do not
|
||||
// want the output to be `prettyPrinted` or anything, unless we're running
|
||||
// tests, so we use a supplied json encoder.
|
||||
|
||||
extension CliClient.RunPlaybook.CreateOptions {
|
||||
|
||||
func createJSONData(
|
||||
configuration: Configuration,
|
||||
encoder: JSONEncoder = .init()
|
||||
) throws -> Data {
|
||||
@Dependency(\.logger) var logger
|
||||
|
||||
let templateDir = template.directory ?? configuration.template.directory
|
||||
let templateRepo = template.url ?? configuration.template.url
|
||||
let version = template.version ?? configuration.template.version
|
||||
|
||||
logger.debug("""
|
||||
(\(useLocalTemplateDirectory), \(String(describing: templateDir)), \(String(describing: templateRepo)))
|
||||
""")
|
||||
|
||||
switch (useLocalTemplateDirectory, templateDir, templateRepo) {
|
||||
case (true, .none, _):
|
||||
// User supplied they wanted to use a local template directory, but we could not find
|
||||
// the path set from command line or in configuration.
|
||||
throw CliClientError.templateDirectoryNotFound
|
||||
case let (false, _, .some(repo)):
|
||||
// User did not supply they wanted to use a local template directory, and we found a repo url that was
|
||||
// either set by the command line or found in the configuration.
|
||||
logger.debug("Using repo.")
|
||||
return try encoder.encode(TemplateRepo(repo: repo, version: version))
|
||||
case let (true, .some(templateDir), _):
|
||||
// User supplied they wanted to use a local template directory, and we found the template directory
|
||||
// either set by the command line or in the configuration.
|
||||
logger.debug("Using template directory.")
|
||||
return try encoder.encode(TemplateDirJson(path: templateDir))
|
||||
case let (false, .some(templateDir), _):
|
||||
// User supplied they did not wanted to use a local template directory, and we found the template directory
|
||||
// either set by the command line or in the configuration, and no repo was found / handled previously.
|
||||
logger.debug("Using template directory.")
|
||||
return try encoder.encode(TemplateDirJson(path: templateDir))
|
||||
case (_, .none, .none):
|
||||
// We could not find a repo or template directory.
|
||||
throw CliClientError.templateDirectoryOrRepoNotSpecified
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct TemplateDirJson: Encodable {
|
||||
|
||||
let template: Template
|
||||
|
||||
init(path: String) {
|
||||
self.template = .init(path: path)
|
||||
}
|
||||
|
||||
struct Template: Encodable {
|
||||
let path: String
|
||||
}
|
||||
}
|
||||
|
||||
private struct TemplateRepo: Encodable {
|
||||
|
||||
let template: Template
|
||||
|
||||
init(repo: String, version: String?) {
|
||||
self.template = .init(repo: .init(url: repo, version: version ?? "main"))
|
||||
}
|
||||
|
||||
struct Template: Encodable {
|
||||
let repo: Repo
|
||||
}
|
||||
|
||||
struct Repo: Encodable {
|
||||
let url: String
|
||||
let version: String
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ public enum CliClientError: Error {
|
||||
case brewfileNotFound
|
||||
case encodingError
|
||||
case playbookDirectoryNotFound
|
||||
case projectDirectoryNotFound
|
||||
case generate(GenerateError)
|
||||
case templateDirectoryNotFound
|
||||
case templateDirectoryOrRepoNotSpecified
|
||||
|
||||
@@ -6,6 +6,7 @@ import Foundation
|
||||
// want the output to be `prettyPrinted` or anything, unless we're running
|
||||
// tests, so we use a supplied json encoder.
|
||||
|
||||
// TODO: Remove.
|
||||
func createJSONData(
|
||||
_ options: CliClient.GenerateJsonOptions,
|
||||
logging loggingOptions: CliClient.LoggingOptions,
|
||||
|
||||
@@ -25,6 +25,60 @@ public struct CliClient: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
public extension CliClient {
|
||||
|
||||
@DependencyClient
|
||||
struct RunPlaybook: Sendable {
|
||||
public var buildProject: @Sendable (RunOptions, BuildOptions) async throws -> Void
|
||||
public var createProject: @Sendable (RunOptions, CreateOptions) async throws -> String
|
||||
|
||||
public struct RunOptions: Equatable, Sendable {
|
||||
public let loggingOptions: CliClient.LoggingOptions
|
||||
public let quiet: Bool
|
||||
public let shell: String?
|
||||
}
|
||||
|
||||
public struct BuildOptions: Equatable, Sendable {
|
||||
public let extraOptions: [String]?
|
||||
public let inventoryFilePath: String?
|
||||
public let projectDirectory: String?
|
||||
|
||||
public init(
|
||||
extraOptions: [String]?,
|
||||
inventoryFilePath: String?,
|
||||
projectDirectory: String
|
||||
) {
|
||||
self.extraOptions = extraOptions
|
||||
self.inventoryFilePath = inventoryFilePath
|
||||
self.projectDirectory = projectDirectory
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct CreateOptions: Equatable, Sendable {
|
||||
public let extraOptions: [String]?
|
||||
public let inventoryFilePath: String?
|
||||
public let projectDirectory: String
|
||||
public let template: Configuration.Template
|
||||
public let useLocalTemplateDirectory: Bool
|
||||
|
||||
public init(
|
||||
extraOptions: [String]?,
|
||||
inventoryFilePath: String?,
|
||||
projectDirectory: String,
|
||||
template: Configuration.Template,
|
||||
useLocalTemplateDirectory: Bool
|
||||
) {
|
||||
self.extraOptions = extraOptions
|
||||
self.inventoryFilePath = inventoryFilePath
|
||||
self.projectDirectory = projectDirectory
|
||||
self.template = template
|
||||
self.useLocalTemplateDirectory = useLocalTemplateDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension CliClient {
|
||||
|
||||
struct PandocOptions: Equatable, Sendable {
|
||||
@@ -125,6 +179,50 @@ public extension CliClient {
|
||||
self.quiet = quiet
|
||||
self.shell = shell
|
||||
}
|
||||
|
||||
public enum Route {
|
||||
case build(BuildOption)
|
||||
case create(CreateOption)
|
||||
|
||||
public struct BuildOption: Equatable, Sendable {
|
||||
public let extraOptions: [String]?
|
||||
public let inventoryFilePath: String?
|
||||
public let projectDirectory: String?
|
||||
|
||||
public init(
|
||||
extraOptions: [String]?,
|
||||
inventoryFilePath: String?,
|
||||
projectDirectory: String
|
||||
) {
|
||||
self.extraOptions = extraOptions
|
||||
self.inventoryFilePath = inventoryFilePath
|
||||
self.projectDirectory = projectDirectory
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct CreateOption: Equatable, Sendable {
|
||||
public let extraOptions: [String]?
|
||||
public let inventoryFilePath: String?
|
||||
public let projectDirectory: String
|
||||
public let template: Configuration.Template
|
||||
public let useLocalTemplateDirectory: Bool
|
||||
|
||||
public init(
|
||||
extraOptions: [String]?,
|
||||
inventoryFilePath: String?,
|
||||
projectDirectory: String,
|
||||
template: Configuration.Template,
|
||||
useLocalTemplateDirectory: Bool
|
||||
) {
|
||||
self.extraOptions = extraOptions
|
||||
self.inventoryFilePath = inventoryFilePath
|
||||
self.projectDirectory = projectDirectory
|
||||
self.template = template
|
||||
self.useLocalTemplateDirectory = useLocalTemplateDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RunCommandOptions: Sendable, Equatable {
|
||||
|
||||
@@ -2,6 +2,7 @@ import Dependencies
|
||||
import Logging
|
||||
import ShellClient
|
||||
|
||||
// TODO: Remove.
|
||||
public extension CliClient {
|
||||
|
||||
@discardableResult
|
||||
|
||||
Reference in New Issue
Block a user