feat: Initial commit

This commit is contained in:
2024-11-29 14:30:52 -05:00
commit 58e0f0e4b5
18 changed files with 732 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
import ArgumentParser
import Dependencies
import Rainbow
import ShellClient
@main
struct Application: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "hpa",
abstract: "A utility for working with ansible hpa playbook.",
subcommands: [BuildCommand.self, CreateProjectCommand.self, CreateProjectTemplateCommand.self]
)
}

View File

@@ -0,0 +1,35 @@
import ArgumentParser
import CliClient
import Dependencies
struct BuildCommand: AsyncParsableCommand {
static let commandName = "build"
static let configuration = CommandConfiguration.playbookCommandConfiguration(
commandName: commandName,
abstract: "Build a home performance assesment project."
)
@OptionGroup var globals: GlobalOptions
@Argument(
help: "The project directory.",
completion: .directory
)
var projectDir: String
@Argument(
help: "Extra arguments passed to the playbook."
)
var extraArgs: [String] = []
mutating func run() async throws {
let args = [
"--tags", "build-project",
"--extra-vars", "project_dir=\(projectDir)"
] + extraArgs
try await runPlaybook(commandName: Self.commandName, globals: globals, args: args)
}
}

View File

@@ -0,0 +1,15 @@
import ArgumentParser
struct CreateProjectCommand: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "create-project",
abstract: "Create a home performance assesment project."
)
@OptionGroup var globals: GlobalOptions
mutating func run() async throws {
fatalError()
}
}

View File

@@ -0,0 +1,15 @@
import ArgumentParser
struct CreateProjectTemplateCommand: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "create-project-template",
abstract: "Create a home performance assesment project template."
)
@OptionGroup var globals: GlobalOptions
mutating func run() async throws {
fatalError()
}
}

View File

@@ -0,0 +1,22 @@
import ArgumentParser
struct GlobalOptions: ParsableArguments {
@Option(
name: .shortAndLong,
help: "Optional path to the ansible hpa playbook directory."
)
var playbookDir: String?
@Option(
name: .shortAndLong,
help: "Optional path to the ansible inventory to use."
)
var inventoryPath: String?
@Flag(
name: .long,
help: "Increase logging level."
)
var verbose: Int
}

109
Sources/hpa/Helpers.swift Normal file
View File

@@ -0,0 +1,109 @@
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<GlobalOptions, String?>,
configurationKeyPath: KeyPath<Configuration, String?>
) 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 {}