Files
swift-hpa/Sources/hpa/Helpers.swift

191 lines
5.0 KiB
Swift

import ArgumentParser
import CliClient
import Dependencies
import Foundation
import Logging
import Rainbow
import ShellClient
extension CommandConfiguration {
static func playbookCommandConfiguration(
commandName: String,
abstract: String,
examples: (label: String, example: String)...
) -> Self {
guard examples.count > 0 else {
fatalError("Did not supply any examples.")
}
return Self(
commandName: commandName,
abstract: "\(abstract.blue)",
discussion: """
\("NOTE:".yellow) Most options are not required if you have a configuration file setup.
\("Examples:".yellow)
\(examples.map { "\($0.label.green.italic)\n $ hpa \($0.example)" }.joined(separator: "\n"))
\("Passing extra args to the playbook.".green.italic)
$ hpa \(examples[0].example) -- --vault-id "myId@$SCRIPTS/vault-gopass-client"
\("See Also:".yellow) \("Ansible playbook options.".italic)
$ ansible-playbook --help
\("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. See examples above.
"""
)
}
}
extension BasicGlobalOptions {
var shellOrDefault: ShellCommand.Shell {
guard let shell else { return .zsh(useDashC: true) }
return .custom(path: shell, useDashC: true)
}
}
extension GlobalOptions {
var shellOrDefault: ShellCommand.Shell { basic.shellOrDefault }
}
func ensureString(
globals: GlobalOptions,
configuration: Configuration,
globalsKeyPath: KeyPath<GlobalOptions, String?>,
configurationKeyPath: KeyPath<Configuration, String?>
) throws -> String {
if let global = globals[keyPath: globalsKeyPath] {
return global
}
guard let configuration = configuration[keyPath: configurationKeyPath] else {
throw RunPlaybookError.playbookNotFound
}
return configuration
}
func withSetupLogger(
commandName: String,
globals: BasicGlobalOptions,
quietOnlyPlaybook: Bool = false,
dependencies setupDependencies: (inout DependencyValues) -> Void = { _ in },
operation: @escaping () async throws -> Void
) async rethrows {
try await withDependencies {
$0.logger = .init(label: "\("hpa".yellow)")
if quietOnlyPlaybook || !globals.quiet {
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: {
try await operation()
}
}
func withSetupLogger(
commandName: String,
globals: GlobalOptions,
dependencies setupDependencies: (inout DependencyValues) -> Void = { _ in },
operation: @escaping () async throws -> Void
) async rethrows {
try await withSetupLogger(
commandName: commandName,
globals: globals.basic,
quietOnlyPlaybook: globals.quietOnlyPlaybook,
dependencies: setupDependencies,
operation: operation
)
}
func runPlaybook(
commandName: String,
globals: GlobalOptions,
configuration: Configuration? = nil,
extraArgs: [String],
_ args: String...
) async throws {
try await runPlaybook(
commandName: commandName,
globals: globals,
configuration: configuration,
extraArgs: extraArgs,
args
)
}
private extension CliClient {
func ensuredConfiguration(_ configuration: Configuration?) throws -> Configuration {
guard let configuration else {
return try loadConfiguration()
}
return configuration
}
}
func runPlaybook(
commandName: String,
globals: GlobalOptions,
configuration: Configuration? = nil,
extraArgs: [String],
_ args: [String]
) async throws {
try await withSetupLogger(commandName: commandName, globals: globals) {
@Dependency(\.cliClient) var cliClient
@Dependency(\.logger) var logger
logger.debug("Begin run playbook: \(globals)")
let configuration = try cliClient.ensuredConfiguration(configuration)
logger.debug("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
]
if let defaultArgs = configuration.defaultPlaybookArgs {
playbookArgs.append(defaultArgs)
}
try await cliClient.runCommand(
quiet: globals.quietOnlyPlaybook ? true : globals.basic.quiet,
shell: globals.shellOrDefault,
playbookArgs + args + extraArgs
)
}
}
enum RunPlaybookError: Error {
case playbookNotFound
case configurationError
}