161 lines
3.9 KiB
Swift
161 lines
3.9 KiB
Swift
import CommandClient
|
|
import ConfigurationClient
|
|
import Dependencies
|
|
import DependenciesMacros
|
|
import FileClient
|
|
|
|
// TODO: Add edit / view routes, possibly create?
|
|
|
|
public extension DependencyValues {
|
|
var vaultClient: VaultClient {
|
|
get { self[VaultClient.self] }
|
|
set { self[VaultClient.self] = newValue }
|
|
}
|
|
}
|
|
|
|
@DependencyClient
|
|
public struct VaultClient: Sendable {
|
|
public var run: Run
|
|
|
|
@DependencyClient
|
|
public struct Run: Sendable {
|
|
public var decrypt: @Sendable (RunOptions) async throws -> String
|
|
public var encrypt: @Sendable (RunOptions) async throws -> String
|
|
}
|
|
|
|
public struct RunOptions: Equatable, Sendable {
|
|
|
|
public let extraOptions: [String]?
|
|
public let loggingOptions: LoggingOptions
|
|
public let outputFilePath: String?
|
|
public let quiet: Bool
|
|
public let shell: String?
|
|
public let vaultFilePath: String?
|
|
|
|
public init(
|
|
extraOptions: [String]? = nil,
|
|
loggingOptions: LoggingOptions,
|
|
outputFilePath: String? = nil,
|
|
quiet: Bool = false,
|
|
shell: String? = nil,
|
|
vaultFilePath: String? = nil
|
|
) {
|
|
self.extraOptions = extraOptions
|
|
self.loggingOptions = loggingOptions
|
|
self.outputFilePath = outputFilePath
|
|
self.quiet = quiet
|
|
self.shell = shell
|
|
self.vaultFilePath = vaultFilePath
|
|
}
|
|
}
|
|
|
|
@_spi(Internal)
|
|
public enum Route: String, Equatable, Sendable {
|
|
case decrypt
|
|
case encrypt
|
|
|
|
public var verb: String { rawValue }
|
|
}
|
|
}
|
|
|
|
extension VaultClient: DependencyKey {
|
|
|
|
public static let testValue: VaultClient = Self(run: Run())
|
|
|
|
public static var liveValue: VaultClient {
|
|
.init(
|
|
run: .init(
|
|
decrypt: { try await $0.run(route: .decrypt) },
|
|
encrypt: { try await $0.run(route: .encrypt) }
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
@_spi(Internal)
|
|
public extension VaultClient {
|
|
enum Constants {
|
|
public static let vaultCommand = "ansible-vault"
|
|
}
|
|
}
|
|
|
|
extension VaultClient.RunOptions {
|
|
|
|
// Sets up the default arguments and runs the `ansible-vault` command,
|
|
// returning the output file path, which is either supplied by the caller
|
|
// or found via the `fileClient.findVaultFileInCurrentDirectory`.
|
|
//
|
|
// This allows the output to be piped into other commands.
|
|
//
|
|
//
|
|
@discardableResult
|
|
func run(route: VaultClient.Route) async throws -> String {
|
|
@Dependency(\.commandClient) var commandClient
|
|
|
|
return try await commandClient.run(
|
|
logging: loggingOptions,
|
|
quiet: quiet,
|
|
shell: shell
|
|
) {
|
|
@Dependency(\.configurationClient) var configurationClient
|
|
@Dependency(\.fileClient) var fileClient
|
|
@Dependency(\.logger) var logger
|
|
|
|
var output: String?
|
|
|
|
let configuration = try await configurationClient.findAndLoad()
|
|
logger.trace("Configuration: \(configuration)")
|
|
|
|
var vaultFilePath: String? = vaultFilePath
|
|
|
|
if vaultFilePath == nil {
|
|
vaultFilePath = try await fileClient
|
|
.findVaultFileInCurrentDirectory()?
|
|
.cleanFilePath
|
|
}
|
|
|
|
guard let vaultFilePath else {
|
|
throw VaultClientError.vaultFileNotFound
|
|
}
|
|
output = vaultFilePath
|
|
|
|
logger.trace("Vault file: \(vaultFilePath)")
|
|
|
|
var arguments = [
|
|
VaultClient.Constants.vaultCommand,
|
|
route.verb
|
|
]
|
|
|
|
if let outputFilePath {
|
|
arguments.append(contentsOf: ["--output", outputFilePath])
|
|
output = outputFilePath
|
|
}
|
|
|
|
if let extraOptions {
|
|
arguments.append(contentsOf: extraOptions)
|
|
}
|
|
|
|
if let vaultArgs = configuration.vault.args {
|
|
arguments.append(contentsOf: vaultArgs)
|
|
}
|
|
|
|
if arguments.contains("encrypt"),
|
|
!arguments.contains("--encrypt-vault-id"),
|
|
let id = configuration.vault.encryptId
|
|
{
|
|
arguments.append(contentsOf: ["--encrypt-vault-id", id])
|
|
}
|
|
|
|
arguments.append(vaultFilePath)
|
|
|
|
logger.trace("Arguments: \(arguments)")
|
|
|
|
return (arguments, output ?? "")
|
|
}
|
|
}
|
|
}
|
|
|
|
enum VaultClientError: Error {
|
|
case vaultFileNotFound
|
|
}
|