feat: Begins moving run commands into cli-client module
This commit is contained in:
@@ -36,6 +36,7 @@ let package = Package(
|
|||||||
name: "CliClient",
|
name: "CliClient",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"CodersClient",
|
"CodersClient",
|
||||||
|
"ConfigurationClient",
|
||||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||||
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
||||||
.product(name: "ShellClient", package: "swift-shell-client"),
|
.product(name: "ShellClient", package: "swift-shell-client"),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import ConfigurationClient
|
||||||
import Dependencies
|
import Dependencies
|
||||||
import DependenciesMacros
|
import DependenciesMacros
|
||||||
|
import FileClient
|
||||||
import Foundation
|
import Foundation
|
||||||
import ShellClient
|
import ShellClient
|
||||||
|
|
||||||
@@ -29,6 +31,128 @@ public struct CliClient: Sendable {
|
|||||||
) async throws {
|
) async throws {
|
||||||
try await runCommand(args, quiet, shell)
|
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 {
|
extension CliClient: DependencyKey {
|
||||||
@@ -65,13 +189,64 @@ extension CliClient: DependencyKey {
|
|||||||
public static let testValue: CliClient = Self()
|
public static let testValue: CliClient = Self()
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CliClientError: Error {
|
private extension ConfigurationClient {
|
||||||
case fileExistsAtPath(String)
|
func ensuredConfiguration(_ optionalConfig: Configuration?) async throws -> Configuration {
|
||||||
case vaultFileNotFound
|
guard let config = optionalConfig else {
|
||||||
|
return try await findAndLoad()
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let jsonEncoder: JSONEncoder = {
|
private extension Configuration {
|
||||||
var encoder = JSONEncoder()
|
|
||||||
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]
|
func ensuredPlaybookDirectory(_ optionalDirectory: String?) throws -> String {
|
||||||
return encoder
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
/// Holds keys associated with environment values.
|
enum Constants {
|
||||||
public enum EnvironmentKey {
|
static let playbookCommand = "ansible-playbook"
|
||||||
public static let xdgConfigHomeKey = "XDG_CONFIG_HOME"
|
static let playbookFileName = "main.yml"
|
||||||
public static let hpaConfigDirKey = "HPA_CONFIG_DIR"
|
static let inventoryFileName = "inventory.ini"
|
||||||
public static let hpaConfigFileKey = "HPA_CONFIG_FILE"
|
static let vaultCommand = "ansible-vault"
|
||||||
}
|
|
||||||
|
|
||||||
/// 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"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,15 @@ extension Coders: DependencyKey {
|
|||||||
public static var liveValue: Self {
|
public static var liveValue: Self {
|
||||||
.init(
|
.init(
|
||||||
jsonDecoder: { JSONDecoder() },
|
jsonDecoder: { JSONDecoder() },
|
||||||
jsonEncoder: { JSONEncoder() },
|
jsonEncoder: { defaultJsonEncoder },
|
||||||
tomlDecoder: { TOMLDecoder() },
|
tomlDecoder: { TOMLDecoder() },
|
||||||
tomlEncoder: { TOMLEncoder() }
|
tomlEncoder: { TOMLEncoder() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let defaultJsonEncoder: JSONEncoder = {
|
||||||
|
var encoder = JSONEncoder()
|
||||||
|
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]
|
||||||
|
return encoder
|
||||||
|
}()
|
||||||
|
|||||||
Reference in New Issue
Block a user