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.repository.directory(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.repository.directory(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 } }