feat: Integrates playbook client into hpa-executable.
This commit is contained in:
@@ -30,6 +30,7 @@ let package = Package(
|
||||
"CliClient",
|
||||
"ConfigurationClient",
|
||||
"FileClient",
|
||||
"PlaybookClient",
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "CliDoc", package: "swift-cli-doc"),
|
||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||
|
||||
@@ -2,7 +2,6 @@ import Constants
|
||||
import Dependencies
|
||||
import DependenciesMacros
|
||||
import Foundation
|
||||
import LoggingExtensions
|
||||
import ShellClient
|
||||
|
||||
public extension DependencyValues {
|
||||
|
||||
@@ -52,6 +52,25 @@ extension PlaybookClient.RunPlaybook.CreateOptions {
|
||||
}
|
||||
}
|
||||
|
||||
extension PlaybookClient.RunPlaybook.GenerateTemplateOptions {
|
||||
func run() async throws {
|
||||
try await shared.run { arguments, _ in
|
||||
arguments.append(contentsOf: [
|
||||
"--tags", "repo-template",
|
||||
"--extra-vars", "output_dir=\(templateDirectory)"
|
||||
])
|
||||
|
||||
if let templateVarsDirectory {
|
||||
arguments.append(contentsOf: ["--extra-vars", "repo_vars_dir=\(templateVarsDirectory)"])
|
||||
}
|
||||
|
||||
if !useVault {
|
||||
arguments.append(contentsOf: ["--extra-vars", "use_vault=false"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PlaybookClient.RunPlaybook.SharedRunOptions {
|
||||
|
||||
func run(_ apply: @Sendable @escaping (inout [String], Configuration) throws -> Void) async throws {
|
||||
|
||||
@@ -44,6 +44,7 @@ public extension PlaybookClient {
|
||||
struct RunPlaybook: Sendable {
|
||||
public var buildProject: @Sendable (BuildOptions) async throws -> Void
|
||||
public var createProject: @Sendable (CreateOptions, JSONEncoder?) async throws -> Void
|
||||
public var generateTemplate: @Sendable (GenerateTemplateOptions) async throws -> Void
|
||||
|
||||
public func createProject(_ options: CreateOptions) async throws {
|
||||
try await createProject(options, nil)
|
||||
@@ -84,26 +85,6 @@ public extension PlaybookClient {
|
||||
self.shared = shared
|
||||
}
|
||||
|
||||
public init(
|
||||
extraOptions: [String]? = nil,
|
||||
inventoryFilePath: String? = nil,
|
||||
loggingOptions: LoggingOptions,
|
||||
quiet: Bool = false,
|
||||
shell: String? = nil,
|
||||
projectDirectory: String? = nil
|
||||
) {
|
||||
self.init(
|
||||
projectDirectory: projectDirectory,
|
||||
shared: .init(
|
||||
extraOptions: extraOptions,
|
||||
inventoryFilePath: inventoryFilePath,
|
||||
loggingOptions: loggingOptions,
|
||||
quiet: quiet,
|
||||
shell: shell
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
public subscript<T>(dynamicMember keyPath: KeyPath<SharedRunOptions, T>) -> T {
|
||||
shared[keyPath: keyPath]
|
||||
}
|
||||
@@ -128,28 +109,28 @@ public extension PlaybookClient {
|
||||
self.useLocalTemplateDirectory = useLocalTemplateDirectory
|
||||
}
|
||||
|
||||
public subscript<T>(dynamicMember keyPath: KeyPath<SharedRunOptions, T>) -> T {
|
||||
shared[keyPath: keyPath]
|
||||
}
|
||||
}
|
||||
|
||||
@dynamicMemberLookup
|
||||
public struct GenerateTemplateOptions: Equatable, Sendable {
|
||||
public let shared: SharedRunOptions
|
||||
public let templateDirectory: String
|
||||
public let templateVarsDirectory: String?
|
||||
public let useVault: Bool
|
||||
|
||||
public init(
|
||||
extraOptions: [String]? = nil,
|
||||
inventoryFilePath: String? = nil,
|
||||
loggingOptions: LoggingOptions,
|
||||
projectDirectory: String,
|
||||
quiet: Bool = false,
|
||||
shell: String? = nil,
|
||||
template: Configuration.Template? = nil,
|
||||
useLocalTemplateDirectory: Bool = false
|
||||
shared: SharedRunOptions,
|
||||
templateDirectory: String,
|
||||
templateVarsDirectory: String? = nil,
|
||||
useVault: Bool = true
|
||||
) {
|
||||
self.init(
|
||||
projectDirectory: projectDirectory,
|
||||
shared: .init(
|
||||
extraOptions: extraOptions,
|
||||
inventoryFilePath: inventoryFilePath,
|
||||
loggingOptions: loggingOptions,
|
||||
quiet: quiet,
|
||||
shell: shell
|
||||
),
|
||||
template: template,
|
||||
useLocalTemplateDirectory: useLocalTemplateDirectory
|
||||
)
|
||||
self.shared = shared
|
||||
self.templateDirectory = templateDirectory
|
||||
self.templateVarsDirectory = templateVarsDirectory
|
||||
self.useVault = useVault
|
||||
}
|
||||
|
||||
public subscript<T>(dynamicMember keyPath: KeyPath<SharedRunOptions, T>) -> T {
|
||||
@@ -173,7 +154,8 @@ extension PlaybookClient.RunPlaybook: DependencyKey {
|
||||
public static var liveValue: PlaybookClient.RunPlaybook {
|
||||
.init(
|
||||
buildProject: { try await $0.run() },
|
||||
createProject: { try await $0.run(encoder: $1) }
|
||||
createProject: { try await $0.run(encoder: $1) },
|
||||
generateTemplate: { try await $0.run() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import ArgumentParser
|
||||
import CliClient
|
||||
import Dependencies
|
||||
import Foundation
|
||||
import PlaybookClient
|
||||
|
||||
struct BuildCommand: AsyncParsableCommand {
|
||||
|
||||
@@ -34,33 +35,14 @@ struct BuildCommand: AsyncParsableCommand {
|
||||
var extraOptions: [String] = []
|
||||
|
||||
mutating func run() async throws {
|
||||
try await _run()
|
||||
}
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
|
||||
private func _run() async throws {
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
|
||||
let projectDir: String
|
||||
if projectDirectory == nil {
|
||||
guard let pwd = ProcessInfo.processInfo.environment["PWD"] else {
|
||||
throw ProjectDirectoryNotSupplied()
|
||||
}
|
||||
projectDir = pwd
|
||||
} else {
|
||||
projectDir = projectDirectory!
|
||||
}
|
||||
|
||||
try await cliClient.runPlaybookCommand(
|
||||
globals.playbookOptions(
|
||||
arguments: [
|
||||
"--tags", "build-project",
|
||||
"--extra-vars", "project_dir=\(projectDir)"
|
||||
],
|
||||
configuration: nil
|
||||
),
|
||||
logging: globals.loggingOptions(commandName: Self.commandName)
|
||||
try await playbookClient.run.buildProject(.init(
|
||||
projectDirectory: projectDirectory,
|
||||
shared: globals.sharedPlaybookRunOptions(
|
||||
commandName: Self.commandName,
|
||||
extraOptions: extraOptions
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
struct ProjectDirectoryNotSupplied: Error {}
|
||||
|
||||
@@ -4,6 +4,7 @@ import ConfigurationClient
|
||||
import Dependencies
|
||||
import Foundation
|
||||
import Logging
|
||||
import PlaybookClient
|
||||
|
||||
struct CreateCommand: AsyncParsableCommand {
|
||||
|
||||
@@ -57,40 +58,16 @@ struct CreateCommand: AsyncParsableCommand {
|
||||
var extraOptions: [String] = []
|
||||
|
||||
mutating func run() async throws {
|
||||
try await _run()
|
||||
}
|
||||
|
||||
private func _run() async throws {
|
||||
@Dependency(\.coders) var coders
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
@Dependency(\.configurationClient) var configurationClient
|
||||
|
||||
let loggingOptions = globals.loggingOptions(commandName: Self.commandName)
|
||||
|
||||
try await cliClient.withLogger(loggingOptions) {
|
||||
@Dependency(\.logger) var logger
|
||||
|
||||
let json = try await cliClient.generateJSON(
|
||||
generateJsonOptions,
|
||||
logging: loggingOptions
|
||||
)
|
||||
|
||||
logger.debug("JSON string: \(json)")
|
||||
|
||||
let arguments = [
|
||||
"--tags", "setup-project",
|
||||
"--extra-vars", "project_dir=\(self.projectDir)",
|
||||
"--extra-vars", "'\(json)'"
|
||||
] + extraOptions
|
||||
|
||||
try await cliClient.runPlaybookCommand(
|
||||
globals.playbookOptions(
|
||||
arguments: arguments,
|
||||
configuration: nil
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
try await playbookClient.run.createProject(.init(
|
||||
projectDirectory: projectDir,
|
||||
shared: globals.sharedPlaybookRunOptions(
|
||||
commandName: Self.commandName,
|
||||
extraOptions: extraOptions
|
||||
),
|
||||
logging: loggingOptions
|
||||
)
|
||||
}
|
||||
template: .init(directory: templateDir),
|
||||
useLocalTemplateDirectory: localTemplateDir
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import ArgumentParser
|
||||
import CliClient
|
||||
import ConfigurationClient
|
||||
import PlaybookClient
|
||||
|
||||
struct BasicGlobalOptions: ParsableArguments {
|
||||
@Flag(
|
||||
@@ -57,6 +59,8 @@ struct GlobalOptions: ParsableArguments {
|
||||
|
||||
}
|
||||
|
||||
// TODO: Update these to use CommandClient.LoggingOptions
|
||||
|
||||
extension GlobalOptions {
|
||||
func loggingOptions(commandName: String) -> CliClient.LoggingOptions {
|
||||
.init(
|
||||
@@ -74,3 +78,39 @@ extension BasicGlobalOptions {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
extension GlobalOptions {
|
||||
|
||||
func playbookOptions(
|
||||
arguments: [String],
|
||||
configuration: Configuration?
|
||||
) -> CliClient.PlaybookOptions {
|
||||
.init(
|
||||
arguments: arguments,
|
||||
configuration: configuration,
|
||||
inventoryFilePath: inventoryPath,
|
||||
playbookDirectory: playbookDirectory,
|
||||
quiet: quietOnlyPlaybook ? true : basic.quiet,
|
||||
shell: basic.shell
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension GlobalOptions {
|
||||
func sharedPlaybookRunOptions(
|
||||
commandName: String,
|
||||
extraOptions: [String]?
|
||||
) -> PlaybookClient.RunPlaybook.SharedRunOptions {
|
||||
return .init(
|
||||
extraOptions: extraOptions,
|
||||
inventoryFilePath: inventoryPath,
|
||||
loggingOptions: .init(
|
||||
commandName: commandName,
|
||||
logLevel: .init(globals: basic, quietOnlyPlaybook: quietOnlyPlaybook)
|
||||
),
|
||||
quiet: basic.quiet,
|
||||
shell: basic.shell
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import CliClient
|
||||
import ConfigurationClient
|
||||
|
||||
extension GlobalOptions {
|
||||
|
||||
func playbookOptions(
|
||||
arguments: [String],
|
||||
configuration: Configuration?
|
||||
) -> CliClient.PlaybookOptions {
|
||||
.init(
|
||||
arguments: arguments,
|
||||
configuration: configuration,
|
||||
inventoryFilePath: inventoryPath,
|
||||
playbookDirectory: playbookDirectory,
|
||||
quiet: quietOnlyPlaybook ? true : basic.quiet,
|
||||
shell: basic.shell
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import ArgumentParser
|
||||
import CliClient
|
||||
import Dependencies
|
||||
import PlaybookClient
|
||||
|
||||
struct GenerateProjectTemplateCommand: AsyncParsableCommand {
|
||||
|
||||
@@ -39,24 +40,16 @@ struct GenerateProjectTemplateCommand: AsyncParsableCommand {
|
||||
var extraOptions: [String] = []
|
||||
|
||||
mutating func run() async throws {
|
||||
@Dependency(\.cliClient) var cliClient
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
|
||||
var arguments = [
|
||||
"--tags", "repo-template",
|
||||
"--extra-vars", "output_dir=\(path)"
|
||||
]
|
||||
|
||||
if let varsDir = templateVars {
|
||||
arguments.append(contentsOf: ["--extra-vars", "repo_vars_dir=\(varsDir)"])
|
||||
}
|
||||
|
||||
if noVault {
|
||||
arguments.append(contentsOf: ["--extra-vars", "use_vault=false"])
|
||||
}
|
||||
|
||||
try await cliClient.runPlaybookCommand(
|
||||
globals.playbookOptions(arguments: arguments, configuration: nil),
|
||||
logging: globals.loggingOptions(commandName: Self.commandName)
|
||||
)
|
||||
try await playbookClient.run.generateTemplate(.init(
|
||||
shared: globals.sharedPlaybookRunOptions(
|
||||
commandName: Self.commandName,
|
||||
extraOptions: extraOptions
|
||||
),
|
||||
templateDirectory: path,
|
||||
templateVarsDirectory: templateVars,
|
||||
useVault: !noVault
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ struct PlaybookClientTests: TestCase {
|
||||
.init(loggingOptions: loggingOptions)
|
||||
}
|
||||
|
||||
static let defaultPlaybookPath = "~/.local/share/hpa/playbook/main.yml"
|
||||
static let defaultInventoryPath = "~/.local/share/hpa/playbook/inventory.ini"
|
||||
static let mockVaultArg = Configuration.mock.vault.args![0]
|
||||
|
||||
@Test(.tags(.repository))
|
||||
func repositoryInstallation() async throws {
|
||||
try await withDependencies {
|
||||
@@ -65,9 +69,9 @@ struct PlaybookClientTests: TestCase {
|
||||
print(arguments)
|
||||
|
||||
#expect(arguments == [
|
||||
"ansible-playbook", "~/.local/share/hpa/playbook/main.yml",
|
||||
"--inventory", "~/.local/share/hpa/playbook/inventory.ini",
|
||||
configuration.vault.args!.first!,
|
||||
"ansible-playbook", Self.defaultPlaybookPath,
|
||||
"--inventory", Self.defaultInventoryPath,
|
||||
Self.mockVaultArg,
|
||||
"--tags", "build-project",
|
||||
"--extra-vars", "project_dir=/foo"
|
||||
])
|
||||
@@ -102,9 +106,9 @@ struct PlaybookClientTests: TestCase {
|
||||
logger.debug("\(arguments)")
|
||||
|
||||
#expect(arguments == [
|
||||
"ansible-playbook", "~/.local/share/hpa/playbook/main.yml",
|
||||
"--inventory", "~/.local/share/hpa/playbook/inventory.ini",
|
||||
configuration.vault.args!.first!,
|
||||
"ansible-playbook", Self.defaultPlaybookPath,
|
||||
"--inventory", Self.defaultInventoryPath,
|
||||
Self.mockVaultArg,
|
||||
"--tags", "setup-project",
|
||||
"--extra-vars", "project_dir=/project",
|
||||
"--extra-vars", json
|
||||
@@ -139,6 +143,33 @@ struct PlaybookClientTests: TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func generateTemplate() async throws {
|
||||
try await withCapturingCommandClient("generateTemplate") {
|
||||
$0.configurationClient = .mock()
|
||||
$0.playbookClient = .liveValue
|
||||
} run: {
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
|
||||
try await playbookClient.run.generateTemplate(.init(
|
||||
shared: Self.sharedRunOptions,
|
||||
templateDirectory: "/template"
|
||||
))
|
||||
|
||||
} assert: { output in
|
||||
|
||||
let expected = [
|
||||
"ansible-playbook", Self.defaultPlaybookPath,
|
||||
"--inventory", Self.defaultInventoryPath,
|
||||
Self.mockVaultArg,
|
||||
"--tags", "repo-template",
|
||||
"--extra-vars", "output_dir=/template"
|
||||
]
|
||||
|
||||
#expect(output.arguments == expected)
|
||||
}
|
||||
}
|
||||
|
||||
func withMockConfiguration(
|
||||
_ capturing: CommandClient.CapturingClient,
|
||||
configuration: Configuration = .mock,
|
||||
|
||||
Reference in New Issue
Block a user