From 56a406b2312bf051a35fefaf3eb6cb34a24c5324 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Sat, 30 Nov 2024 19:27:18 -0500 Subject: [PATCH] feat: Working encrypt and decrypt commands. --- Sources/CliClient/CliClient.swift | 13 ++--- Sources/CliClient/Configuration.swift | 8 +++- .../CommandConfigurationExtensions.swift | 48 +++++++++++++++---- Sources/hpa/Internal/LoggingExtensions.swift | 3 +- Sources/hpa/Internal/RunVault.swift | 21 +++++--- .../hpa/VaultCommands/DecryptCommand.swift | 10 +++- .../hpa/VaultCommands/EncryptCommand.swift | 10 +++- 7 files changed, 87 insertions(+), 26 deletions(-) diff --git a/Sources/CliClient/CliClient.swift b/Sources/CliClient/CliClient.swift index 3a00939..216cefe 100644 --- a/Sources/CliClient/CliClient.swift +++ b/Sources/CliClient/CliClient.swift @@ -68,13 +68,14 @@ extension CliClient: DependencyKey { in: nil, args )) + } else { + try await shellClient.background(.init( + shell: shell, + environment: ProcessInfo.processInfo.environment, + in: nil, + args + )) } - try await shellClient.background(.init( - shell: shell, - environment: ProcessInfo.processInfo.environment, - in: nil, - args - )) } createConfiguration: { path, json in // Early out if a file exists at the path already. diff --git a/Sources/CliClient/Configuration.swift b/Sources/CliClient/Configuration.swift index f466505..a029912 100644 --- a/Sources/CliClient/Configuration.swift +++ b/Sources/CliClient/Configuration.swift @@ -12,6 +12,7 @@ public struct Configuration: Codable, Sendable { public let templateDir: String? public let defaultPlaybookArgs: [String]? public let defaultVaultArgs: [String]? + public let defaultVaultEncryptId: String? fileprivate enum CodingKeys: String, CodingKey { case playbookDir = "HPA_PLAYBOOK_DIR" @@ -21,6 +22,7 @@ public struct Configuration: Codable, Sendable { case templateDir = "HPA_TEMPLATE_DIR" case defaultPlaybookArgs = "HPA_DEFAULT_PLAYBOOK_ARGS" case defaultVaultArgs = "HPA_DEFAULT_VAULT_ARGS" + case defaultVaultEncryptId = "HPA_DEFAULT_VAULT_ENCRYPT_ID" } public static func fromEnv( @@ -40,7 +42,8 @@ public struct Configuration: Codable, Sendable { templateRepoVersion: hpaValues.value(for: .templateRepoVersion), templateDir: hpaValues.value(for: .templateDir), defaultPlaybookArgs: hpaValues.array(for: .defaultPlaybookArgs), - defaultVaultArgs: hpaValues.array(for: .defaultVaultArgs) + defaultVaultArgs: hpaValues.array(for: .defaultVaultArgs), + defaultVaultEncryptId: hpaValues.value(for: .defaultVaultEncryptId) ) } @@ -52,7 +55,8 @@ public struct Configuration: Codable, Sendable { templateRepoVersion: "main", templateDir: "/path/to/local/template", defaultPlaybookArgs: ["--tags", "debug"], - defaultVaultArgs: ["--vault-id=myId@$SCRIPTS/vault-gopass-client"] + defaultVaultArgs: ["--vault-id=myId@$SCRIPTS/vault-gopass-client"], + defaultVaultEncryptId: "myId" ) } diff --git a/Sources/hpa/Internal/CommandConfigurationExtensions.swift b/Sources/hpa/Internal/CommandConfigurationExtensions.swift index cc1e92d..bf55de1 100644 --- a/Sources/hpa/Internal/CommandConfigurationExtensions.swift +++ b/Sources/hpa/Internal/CommandConfigurationExtensions.swift @@ -3,6 +3,19 @@ import Rainbow extension CommandConfiguration { + static func create( + commandName: String, + abstract: String, + usesExtraArgs: Bool = true, + discussion: Discussion + ) -> Self { + .init( + commandName: commandName, + abstract: createAbstract(abstract), + discussion: discussion.render() + ) + } + static func create( commandName: String, abstract: String, @@ -25,12 +38,12 @@ extension CommandConfiguration { .create( commandName: commandName, abstract: abstract, - discussion: [.note(label: "Most options are not required if you have a configuration file setup.")] - + examples.nodes(parentCommand) - + [ - .seeAlso(label: "Ansible playbook options.", command: "ansible-playbook"), - .important(label: Constants.importantExtraArgsNote) - ] + discussion: .default( + usesExtraArgs: true, + parentCommand: parentCommand, + examples: examples, + seeAlso: .seeAlso(label: "Ansible playbook options.", command: "ansible-playbook") + ) ) } } @@ -42,6 +55,21 @@ func createAbstract(_ string: String) -> String { struct Discussion { let nodes: [Node] + static func `default`( + usesExtraArgs: Bool, + parentCommand: String?, + examples: [(label: String, example: String)], + seeAlso: Node? + ) -> Self { + var nodes = Array.defaultNodes + examples.nodes(parentCommand) + if let seeAlso { nodes.append(seeAlso) } + if usesExtraArgs && examples.count > 0 { nodes.append(.important(label: Constants.importantExtraArgsNote)) } + return .init( + nodes: nodes, + usesExtraArgs: usesExtraArgs + ) + } + init(usesExtraArgs: Bool = true, _ nodes: Node...) { self.init(nodes: nodes, usesExtraArgs: usesExtraArgs) } @@ -225,9 +253,13 @@ private extension Array where Element == (label: String, example: String) { } } -private extension Array where Element == Node { +extension Array where Element == Node { - func render(separator: String = "\n\n") -> String { + static var defaultNodes: Self { + [.note(label: "Most options are not required if you have a configuration file setup.")] + } + + fileprivate func render(separator: String = "\n\n") -> String { map { // Strip of any new-line characters from the last section of the rendered string // of the node. This allows us to have a consistent single new-line between each diff --git a/Sources/hpa/Internal/LoggingExtensions.swift b/Sources/hpa/Internal/LoggingExtensions.swift index 6706cf7..d749bad 100644 --- a/Sources/hpa/Internal/LoggingExtensions.swift +++ b/Sources/hpa/Internal/LoggingExtensions.swift @@ -5,7 +5,7 @@ extension Logger.Level { /// Set the log level based on the user's options supplied. init(globals: BasicGlobalOptions, quietOnlyPlaybook: Bool) { - if quietOnlyPlaybook || !globals.quiet { + if !quietOnlyPlaybook && !globals.quiet { switch globals.verbose { case 0: self = .info @@ -16,6 +16,7 @@ extension Logger.Level { default: self = .info } + return } self = .info } diff --git a/Sources/hpa/Internal/RunVault.swift b/Sources/hpa/Internal/RunVault.swift index 9416eef..39cb5ed 100644 --- a/Sources/hpa/Internal/RunVault.swift +++ b/Sources/hpa/Internal/RunVault.swift @@ -26,16 +26,23 @@ func runVault( let defaultArgs = configuration.defaultVaultArgs ?? [] + var vaultArgs = ["ansible-vault"] + + args + + defaultArgs + + options.extraArgs + + [path] + + if args.contains("encrypt"), + !vaultArgs.contains("--encrypt-vault-id"), + let id = configuration.defaultVaultEncryptId + { + vaultArgs.append(contentsOf: ["--encrypt-vault-id", id]) + } + try await cliClient.runCommand( quiet: options.quiet, shell: options.shellOrDefault, - ["ansible-vault"] - + args - + defaultArgs - + options.extraArgs - + [path] + vaultArgs ) - - fatalError() } } diff --git a/Sources/hpa/VaultCommands/DecryptCommand.swift b/Sources/hpa/VaultCommands/DecryptCommand.swift index b06ffd9..95c8124 100644 --- a/Sources/hpa/VaultCommands/DecryptCommand.swift +++ b/Sources/hpa/VaultCommands/DecryptCommand.swift @@ -18,6 +18,14 @@ struct DecryptCommand: AsyncParsableCommand { var output: String? mutating func run() async throws { - fatalError() + var args = ["decrypt"] + if let output { + args.append(contentsOf: ["--output", output]) + } + try await runVault( + commandName: Self.commandName, + options: options, + args + ) } } diff --git a/Sources/hpa/VaultCommands/EncryptCommand.swift b/Sources/hpa/VaultCommands/EncryptCommand.swift index 19880f4..2d24a2a 100644 --- a/Sources/hpa/VaultCommands/EncryptCommand.swift +++ b/Sources/hpa/VaultCommands/EncryptCommand.swift @@ -18,6 +18,14 @@ struct EncryptCommand: AsyncParsableCommand { var output: String? mutating func run() async throws { - fatalError() + var args = ["encrypt"] + if let output { + args.append(contentsOf: ["--output", output]) + } + try await runVault( + commandName: Self.commandName, + options: options, + args + ) } }