Files
swift-hpa/Sources/CliClient/CliClient+RunPlaybook.swift

217 lines
6.4 KiB
Swift

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
}
}