feat: Initial commit
This commit is contained in:
15
Sources/hpa/Application.swift
Normal file
15
Sources/hpa/Application.swift
Normal 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]
|
||||
)
|
||||
|
||||
}
|
||||
35
Sources/hpa/BuildCommand.swift
Normal file
35
Sources/hpa/BuildCommand.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
15
Sources/hpa/CreateProjectCommand.swift
Normal file
15
Sources/hpa/CreateProjectCommand.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
15
Sources/hpa/CreateTemplateCommand.swift
Normal file
15
Sources/hpa/CreateTemplateCommand.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
22
Sources/hpa/GlobalOptions.swift
Normal file
22
Sources/hpa/GlobalOptions.swift
Normal 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
109
Sources/hpa/Helpers.swift
Normal 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 {}
|
||||
Reference in New Issue
Block a user