From 56a0bca00c0665d030762e05696780c5715473d6 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Thu, 12 Dec 2024 12:37:14 -0500 Subject: [PATCH] feat: Fixes not creating default config directory --- .../ConfigurationClient.swift | 25 ++++++++++-- Sources/ConfigurationClient/File.swift | 8 +++- Sources/FileClient/FileClient.swift | 12 +++++- .../UtilsCommands/CreateTemplateCommand.swift | 34 +++++++++-------- .../UtilsCommands/GenerateConfigCommand.swift | 38 +++++++++++-------- justfile | 3 ++ 6 files changed, 85 insertions(+), 35 deletions(-) diff --git a/Sources/ConfigurationClient/ConfigurationClient.swift b/Sources/ConfigurationClient/ConfigurationClient.swift index fd63899..0f5b0c2 100644 --- a/Sources/ConfigurationClient/ConfigurationClient.swift +++ b/Sources/ConfigurationClient/ConfigurationClient.swift @@ -117,12 +117,31 @@ struct LiveConfigurationClient { } func generate(at file: File, force: Bool) async throws { + @Dependency(\.logger) var logger + + logger.debug("Begin generating configuration: \(file.path), force: \(force)") + + let expandedPath = file.path.replacingOccurrences( + of: "~", + with: fileManager.homeDirectory().cleanFilePath + ) + + let fileUrl = URL(filePath: expandedPath) + if !force { - guard !fileManager.fileExists(file.url) else { + guard !fileManager.fileExists(fileUrl) else { throw ConfigurationError.fileExists(path: file.path) } } + let fileDirectory = fileUrl.deletingLastPathComponent() + let directoryExists = try await fileManager.isDirectory(fileDirectory) + + if !directoryExists { + logger.debug("Creating directory at: \(fileDirectory.cleanFilePath)") + try await fileManager.createDirectory(fileDirectory) + } + if case .toml = file { // In the case of toml, we copy the internal resource that includes // usage comments in the file. @@ -133,11 +152,11 @@ struct LiveConfigurationClient { throw ConfigurationError.resourceNotFound } - try await fileManager.copy(resourceFile, file.url) + try await fileManager.copy(resourceFile, fileUrl) } else { // Json does not allow comments, so we write the mock configuration // to the file path. - try await write(.mock, to: file, force: force) + try await write(.mock, to: File(fileUrl)!, force: force) } } diff --git a/Sources/ConfigurationClient/File.swift b/Sources/ConfigurationClient/File.swift index 6cf5c82..13666bf 100644 --- a/Sources/ConfigurationClient/File.swift +++ b/Sources/ConfigurationClient/File.swift @@ -40,7 +40,13 @@ public enum File: Equatable, Sendable { } public static var `default`: Self { - .toml("~/.config/\(HPAKey.configDirName)/\(HPAKey.defaultFileName)") + let fileUrl = FileManager.default + .homeDirectoryForCurrentUser + .appending(path: ".config") + .appending(path: HPAKey.configDirName) + .appending(path: HPAKey.defaultFileName) + + return .toml(fileUrl) } } diff --git a/Sources/FileClient/FileClient.swift b/Sources/FileClient/FileClient.swift index 64152f0..c533b6b 100644 --- a/Sources/FileClient/FileClient.swift +++ b/Sources/FileClient/FileClient.swift @@ -12,9 +12,11 @@ public extension DependencyValues { @DependencyClient public struct FileClient: Sendable { public var copy: @Sendable (URL, URL) async throws -> Void + public var createDirectory: @Sendable (URL) async throws -> Void public var fileExists: @Sendable (URL) -> Bool = { _ in true } public var findVaultFileInCurrentDirectory: @Sendable () async throws -> URL? public var homeDirectory: @Sendable () -> URL = { URL(filePath: "~/") } + public var isDirectory: @Sendable (URL) async throws -> Bool public var load: @Sendable (URL) async throws -> Data public var write: @Sendable (Data, URL) async throws -> Void } @@ -26,12 +28,16 @@ extension FileClient: DependencyKey { let manager = LiveFileClient() return .init { try await manager.copy($0, to: $1) + } createDirectory: { + try await manager.creatDirectory($0) } fileExists: { url in manager.fileExists(at: url) } findVaultFileInCurrentDirectory: { try await manager.findVaultFileInCurrentDirectory() } homeDirectory: { manager.homeDirectory() + } isDirectory: { + manager.isDirectory($0) } load: { url in try await manager.load(from: url) } write: { data, url in @@ -48,6 +54,10 @@ struct LiveFileClient: Sendable { try manager.copyItem(at: url, to: toUrl) } + func creatDirectory(_ url: URL) async throws { + try manager.createDirectory(at: url, withIntermediateDirectories: true) + } + func fileExists(at url: URL) -> Bool { manager.fileExists(atPath: url.cleanFilePath) } @@ -78,7 +88,7 @@ struct LiveFileClient: Sendable { manager.homeDirectoryForCurrentUser } - private func isDirectory(_ url: URL) -> Bool { + func isDirectory(_ url: URL) -> Bool { var isDirectory: ObjCBool = false manager.fileExists(atPath: url.cleanFilePath, isDirectory: &isDirectory) return isDirectory.boolValue diff --git a/Sources/hpa/UtilsCommands/CreateTemplateCommand.swift b/Sources/hpa/UtilsCommands/CreateTemplateCommand.swift index 86f4e72..a599abf 100644 --- a/Sources/hpa/UtilsCommands/CreateTemplateCommand.swift +++ b/Sources/hpa/UtilsCommands/CreateTemplateCommand.swift @@ -1,4 +1,6 @@ import ArgumentParser +import CliClient +import Dependencies struct GenerateProjectTemplateCommand: AsyncParsableCommand { @@ -36,23 +38,25 @@ struct GenerateProjectTemplateCommand: AsyncParsableCommand { ) var extraArgs: [String] = [] - // FIX: mutating func run() async throws { - let varsDir = templateVars != nil - ? ["--extra-vars", "repo_vars_dir=\(templateVars!)"] - : [] + @Dependency(\.cliClient) var cliClient - let vault = noVault ? ["--extra-vars", "use_vault=false"] : [] + var arguments = [ + "--tags", "repo-template", + "--extra-vars", "output_dir=\(path)" + ] -// try await runPlaybook( -// commandName: Self.commandName, -// globals: globals, -// extraArgs: extraArgs, -// [ -// "--tags", "repo-template", -// "--extra-vars", "output_dir=\(path)" -// ] + varsDir -// + vault -// ) + 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) + ) } } diff --git a/Sources/hpa/UtilsCommands/GenerateConfigCommand.swift b/Sources/hpa/UtilsCommands/GenerateConfigCommand.swift index e993f8c..efd36c6 100644 --- a/Sources/hpa/UtilsCommands/GenerateConfigCommand.swift +++ b/Sources/hpa/UtilsCommands/GenerateConfigCommand.swift @@ -1,6 +1,7 @@ import ArgumentParser import CliClient import CliDoc +import ConfigurationClient import Dependencies struct GenerateConfigurationCommand: AsyncParsableCommand { @@ -42,32 +43,39 @@ struct GenerateConfigurationCommand: AsyncParsableCommand { ) var json: Bool = false + @Flag( + name: .shortAndLong, + help: "Force generation, overwriting a file if it exists." + ) + var force: Bool = false + @OptionGroup var globals: BasicGlobalOptions mutating func run() async throws { try await _run() } - // FIX: private func _run() async throws { @Dependency(\.cliClient) var cliClient + @Dependency(\.configurationClient) var configurationClient + try await cliClient.withLogger(globals.loggingOptions(commandName: Self.commandName)) { - let actualPath: String + @Dependency(\.logger) var logger -// if let path { -// actualPath = "\(path)/config" -// } else { -// let path = "~/.config/hpa-playbook/" -// try await cliClient.runCommand( -// quiet: false, -// shell: globals.shellOrDefault, -// "mkdir", "-p", path -// ) -// actualPath = "\(path)/config" -// } + let actualPath: File - fatalError() - // try cliClient.createConfiguration(actualPath, json) + if let path, let file = File("\(path)/config.\(json ? "json" : "toml")") { + actualPath = file + } else { + actualPath = .default + } + + logger.debug("Generating config at path: \(actualPath.path)") + + try await configurationClient.generate( + at: actualPath, + force: force + ) } } } diff --git a/justfile b/justfile index 051c6b3..47db532 100644 --- a/justfile +++ b/justfile @@ -13,3 +13,6 @@ run *ARGS: swift run hpa {{ARGS}} alias r := run + +clean: + rm -rf .build