feat: Begins moving run commands into cli-client module

This commit is contained in:
2024-12-11 12:31:41 -05:00
parent 9c784d4dcb
commit ddb5e6767a
4 changed files with 196 additions and 21 deletions

View File

@@ -1,5 +1,7 @@
import ConfigurationClient
import Dependencies
import DependenciesMacros
import FileClient
import Foundation
import ShellClient
@@ -29,6 +31,128 @@ public struct CliClient: Sendable {
) async throws {
try await runCommand(args, quiet, shell)
}
public func runPlaybookCommand(_ options: PlaybookOptions) async throws {
@Dependency(\.configurationClient) var configurationClient
@Dependency(\.logger) var logger
let configuration = try await configurationClient.ensuredConfiguration(options.configuration)
logger.trace("Configuration: \(configuration)")
let playbookDirectory = try configuration.ensuredPlaybookDirectory(options.playbookDirectory)
let playbookPath = "\(playbookDirectory)/\(Constants.playbookFileName)"
logger.trace("Playbook path: \(playbookPath)")
let inventoryPath = ensuredInventoryPath(
options.inventoryFilePath,
configuration: configuration,
playboodDirectory: playbookDirectory
)
logger.trace("Inventory path: \(inventoryPath)")
var arguments = [
Constants.playbookCommand, playbookPath,
"--inventory", inventoryPath
] + options.arguments
if let defaultArgs = configuration.args {
arguments.append(contentsOf: defaultArgs)
}
if configuration.useVaultArgs, let vaultArgs = configuration.vault.args {
arguments.append(contentsOf: vaultArgs)
}
logger.trace("Running playbook command with arguments: \(arguments)")
try await runCommand(
quiet: options.quiet,
shell: options.shell.shellOrDefault,
arguments
)
}
public func runVaultCommand(_ options: VaultOptions) async throws {
@Dependency(\.configurationClient) var configurationClient
@Dependency(\.fileClient) var fileClient
@Dependency(\.logger) var logger
let configuration = try await configurationClient.ensuredConfiguration(options.configuration)
logger.trace("Configuration: \(configuration)")
let vaultFilePath = try await fileClient.ensuredVaultFilePath(options.vaultFilePath)
logger.trace("Vault file: \(vaultFilePath)")
var arguments = [
Constants.vaultCommand
] + options.arguments
if let defaultArgs = configuration.vault.args {
arguments.append(contentsOf: defaultArgs)
}
if arguments.contains("encrypt"),
!arguments.contains("--encrypt-vault-id"),
let id = configuration.vault.encryptId
{
arguments.append(contentsOf: ["--encrypt-vault-id", id])
}
try await runCommand(
quiet: options.quiet,
shell: options.shell.shellOrDefault,
arguments
)
}
}
public extension CliClient {
struct PlaybookOptions: Sendable, Equatable {
let arguments: [String]
let configuration: Configuration?
let inventoryFilePath: String?
let playbookDirectory: String?
let quiet: Bool
let shell: String?
public init(
arguments: [String],
configuration: Configuration? = nil,
inventoryFilePath: String? = nil,
playbookDirectory: String? = nil,
quiet: Bool,
shell: String? = nil
) {
self.arguments = arguments
self.configuration = configuration
self.inventoryFilePath = inventoryFilePath
self.playbookDirectory = playbookDirectory
self.quiet = quiet
self.shell = shell
}
}
struct VaultOptions: Equatable, Sendable {
let arguments: [String]
let configuration: Configuration?
let quiet: Bool
let shell: String?
let vaultFilePath: String?
public init(
arguments: [String],
configuration: Configuration? = nil,
quiet: Bool,
shell: String?,
vaultFilePath: String? = nil
) {
self.arguments = arguments
self.configuration = configuration
self.quiet = quiet
self.shell = shell
self.vaultFilePath = vaultFilePath
}
}
}
extension CliClient: DependencyKey {
@@ -65,13 +189,64 @@ extension CliClient: DependencyKey {
public static let testValue: CliClient = Self()
}
enum CliClientError: Error {
case fileExistsAtPath(String)
case vaultFileNotFound
private extension ConfigurationClient {
func ensuredConfiguration(_ optionalConfig: Configuration?) async throws -> Configuration {
guard let config = optionalConfig else {
return try await findAndLoad()
}
return config
}
}
private let jsonEncoder: JSONEncoder = {
var encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]
return encoder
}()
private extension Configuration {
func ensuredPlaybookDirectory(_ optionalDirectory: String?) throws -> String {
guard let directory = optionalDirectory else {
guard let directory = playbook?.directory else {
throw CliClientError.playbookDirectoryNotFound
}
return directory
}
return directory
}
}
private extension Optional where Wrapped == String {
var shellOrDefault: ShellCommand.Shell {
guard let shell = self else { return .zsh(useDashC: true) }
return .custom(path: shell, useDashC: true)
}
}
private func ensuredInventoryPath(
_ optionalInventoryPath: String?,
configuration: Configuration,
playboodDirectory: String
) -> String {
guard let path = optionalInventoryPath else {
guard let path = configuration.playbook?.inventory else {
return "\(playboodDirectory)/\(Constants.inventoryFileName)"
}
return path
}
return path
}
private extension FileClient {
func ensuredVaultFilePath(_ optionalPath: String?) async throws -> String {
guard let path = optionalPath else {
guard let url = try await findVaultFileInCurrentDirectory() else {
throw CliClientError.vaultFileNotFound
}
return url.cleanFilePath
}
return path
}
}
enum CliClientError: Error {
case playbookDirectoryNotFound
case vaultFileNotFound
}

View File

@@ -1,13 +1,6 @@
/// Holds keys associated with environment values.
public enum EnvironmentKey {
public static let xdgConfigHomeKey = "XDG_CONFIG_HOME"
public static let hpaConfigDirKey = "HPA_CONFIG_DIR"
public static let hpaConfigFileKey = "HPA_CONFIG_FILE"
}
/// Holds keys associated with hpa configuration files.
public enum HPAKey {
public static let defaultConfigHome = "~/.config/\(Self.hpaConfigDirectoryName)/\(Self.defaultConfigFileName)"
public static let defaultConfigFileName = "config.toml"
public static let hpaConfigDirectoryName = "hpa"
enum Constants {
static let playbookCommand = "ansible-playbook"
static let playbookFileName = "main.yml"
static let inventoryFileName = "inventory.ini"
static let vaultCommand = "ansible-vault"
}