import ArgumentParser import CliClient import Dependencies import Foundation import Logging import Rainbow import ShellClient extension CommandConfiguration { static func playbookCommandConfiguration(commandName: String, abstract: String) -> Self { Self( commandName: commandName, abstract: "\(abstract.blue)", discussion: """ \("IMPORTANT NOTE:".red) Any extra arguments to pass to the playbook invocation have to be at the end with `--` before any arguments otherwise there will be an "Unkown option" error. \("Example of passing extra args to the playbook:".yellow) $ hpa \(commandName) /my/project -- --vault-id "myId@$SCRIPTS/vault-gopass-client" \("See Also:".yellow) You can run the following command to see the options that can be passed to the playbook invocation. $ ansible-playbook --help """ ) } } func ensureString( globals: GlobalOptions, configuration: Configuration, globalsKeyPath: KeyPath, configurationKeyPath: KeyPath ) throws -> String { if let global = globals[keyPath: globalsKeyPath] { return global } guard let configuration = configuration[keyPath: configurationKeyPath] else { throw PlaybookNotFound() } return configuration } func runPlaybook( commandName: String, globals: GlobalOptions, args: [String] ) async throws { try await withDependencies { $0.logger = .init(label: "\("hpa".yellow)") switch globals.verbose { case 0: $0.logger.logLevel = .info case 1: $0.logger.logLevel = .debug case 2: $0.logger.logLevel = .trace default: $0.logger.logLevel = .info } $0.logger[metadataKey: "command"] = "\(commandName.blue)" } operation: { @Dependency(\.cliClient) var cliClient @Dependency(\.logger) var logger @Dependency(\.asyncShellClient) var shellClient logger.debug("Begin run playbook: \(globals)") let configuration = try cliClient.loadConfiguration() logger.debug("Loaded configuration: \(configuration)") let playbookDir = try ensureString( globals: globals, configuration: configuration, globalsKeyPath: \.playbookDir, configurationKeyPath: \.playbookDir ) let playbook = "\(playbookDir)/main.yml" let inventory = (try? ensureString( globals: globals, configuration: configuration, globalsKeyPath: \.inventoryPath, configurationKeyPath: \.inventoryPath )) ?? "\(playbookDir)/inventory.ini" var playbookArgs = [ "ansible-playbook", playbook, "--inventory", inventory ] + args if let defaultArgs = configuration.defaultPlaybookArgs { playbookArgs.append(defaultArgs) } try await shellClient.foreground(.init( shell: .zsh(useDashC: true), environment: ProcessInfo.processInfo.environment, in: nil, playbookArgs )) } } struct PlaybookNotFound: Error {}