From 847ddbc7b52b9834e775f798bab23bb63228d269 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Fri, 20 Dec 2024 12:54:10 -0500 Subject: [PATCH] feat: Begins update for more modern swift-dependencies implementation. --- .editorconfig | 7 + .gitignore | 1 + .swiftformat | 11 + .swiftlint.yml | 11 + .../swift-cli-version-Package.xcscheme | 3 +- Package.resolved | 25 +- Package.swift | 8 +- .../BuildWithVersionPlugin.swift | 8 +- Sources/CliVersion/CliClient.swift | 222 ++++++++++++++++++ Sources/CliVersion/FileClient.swift | 77 +++--- Sources/CliVersion/GitVersionClient.swift | 23 +- Sources/CliVersion/Helpers.swift | 40 ++++ Sources/TestSupport/TestSupport.swift | 23 ++ Sources/cli-version/BuildCommand.swift | 16 +- Sources/cli-version/CliVersionCommand.swift | 20 +- Sources/cli-version/GenerateCommand.swift | 8 +- Sources/cli-version/Helpers.swift | 3 +- Sources/cli-version/UpdateCommand.swift | 10 +- Tests/CliVersionTests/CliVersionTests.swift | 53 ++--- 19 files changed, 439 insertions(+), 130 deletions(-) create mode 100644 .editorconfig create mode 100644 .swiftformat create mode 100644 .swiftlint.yml create mode 100644 Sources/CliVersion/CliClient.swift create mode 100644 Sources/CliVersion/Helpers.swift create mode 100644 Sources/TestSupport/TestSupport.swift diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7cfbe01 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*.swift] +indent_style = space +indent_size = 2 +tab_width = 2 +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore index 3b29812..7321ea2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc +.nvim/* diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..08f338e --- /dev/null +++ b/.swiftformat @@ -0,0 +1,11 @@ +--self init-only +--indent 2 +--ifdef indent +--trimwhitespace always +--wraparguments before-first +--wrapparameters before-first +--wrapcollections preserve +--wrapconditions after-first +--typeblanklines preserve +--commas inline +--stripunusedargs closure-only diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..213129c --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,11 @@ +disabled_rules: + - closing_brace + - fuction_body_length + - opening_brace + - nesting + +included: + - Sources + - Tests + +ignore_multiline_statement_conditions: true diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/swift-cli-version-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/swift-cli-version-Package.xcscheme index 6902be2..7fc889d 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/swift-cli-version-Package.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/swift-cli-version-Package.xcscheme @@ -96,7 +96,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/Package.resolved b/Package.resolved index fabd13a..ceaf725 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/combine-schedulers", "state" : { - "revision" : "9dc9cbe4bc45c65164fa653a563d8d8db61b09bb", - "version" : "1.0.0" + "revision" : "9fa31f4403da54855f1e2aeaeff478f4f0e40b13", + "version" : "1.0.2" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-clocks", "state" : { - "revision" : "d1fd837326aa719bee979bdde1f53cd5797443eb", - "version" : "1.0.0" + "revision" : "b9b24b69e2adda099a1fa381cda1eeec272d5b53", + "version" : "1.0.5" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies.git", "state" : { - "revision" : "4e1eb6e28afe723286d8cc60611237ffbddba7c5", - "version" : "1.0.0" + "revision" : "5526c8a27675dc7b18d6fa643abfb64bcb200b77", + "version" : "1.6.2" } }, { @@ -99,13 +99,22 @@ "version" : "0.1.4" } }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "0687f71944021d616d34d922343dcef086855920", + "version" : "600.0.1" + } + }, { "identity" : "xctest-dynamic-overlay", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "23cbf2294e350076ea4dbd7d5d047c1e76b03631", - "version" : "1.0.2" + "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1", + "version" : "1.4.3" } } ], diff --git a/Package.swift b/Package.swift index 94e1546..cebaf94 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.6 +// swift-tools-version: 5.10 import PackageDescription @@ -14,6 +14,7 @@ let package = Package( .plugin(name: "UpdateVersionPlugin", targets: ["UpdateVersionPlugin"]) ], dependencies: [ + .package(url: "https://github.com/pointfreeco/swift-dependencies.git", from: "1.6.2"), .package(url: "https://github.com/m-housh/swift-shell-client.git", from: "0.1.3"), .package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2") @@ -26,15 +27,18 @@ let package = Package( .product(name: "ArgumentParser", package: "swift-argument-parser") ] ), + .target(name: "TestSupport"), .target( name: "CliVersion", dependencies: [ + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "DependenciesMacros", package: "swift-dependencies"), .product(name: "ShellClient", package: "swift-shell-client") ] ), .testTarget( name: "CliVersionTests", - dependencies: ["CliVersion"] + dependencies: ["CliVersion", "TestSupport"] ), .plugin( name: "BuildWithVersionPlugin", diff --git a/Plugins/BuildWithVersionPlugin/BuildWithVersionPlugin.swift b/Plugins/BuildWithVersionPlugin/BuildWithVersionPlugin.swift index a866a46..6a3e596 100644 --- a/Plugins/BuildWithVersionPlugin/BuildWithVersionPlugin.swift +++ b/Plugins/BuildWithVersionPlugin/BuildWithVersionPlugin.swift @@ -8,16 +8,16 @@ struct GenerateVersionBuildPlugin: BuildToolPlugin { target: PackagePlugin.Target ) async throws -> [PackagePlugin.Command] { guard let target = target as? SourceModuleTarget else { return [] } - + let gitDirectoryPath = target.directory .removingLastComponent() .removingLastComponent() - + let tool = try context.tool(named: "cli-version") let outputPath = context.pluginWorkDirectory - + let outputFile = outputPath.appending("Version.swift") - + return [ .buildCommand( displayName: "Build With Version Plugin", diff --git a/Sources/CliVersion/CliClient.swift b/Sources/CliVersion/CliClient.swift new file mode 100644 index 0000000..03fa31d --- /dev/null +++ b/Sources/CliVersion/CliClient.swift @@ -0,0 +1,222 @@ +import Foundation +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif +import Dependencies +import DependenciesMacros +import ShellClient + +public extension DependencyValues { + + var cliClient: CliClient { + get { self[CliClient.self] } + set { self[CliClient.self] = newValue } + } +} + +@DependencyClient +public struct CliClient { + public var build: @Sendable (BuildOptions) throws -> String + public var generate: @Sendable (GenerateOptions) throws -> String + public var update: @Sendable (UpdateOptions) throws -> String +} + +extension CliClient: DependencyKey { + public static let testValue: CliClient = Self() + + public static func live(environment: [String: String]) -> Self { + .init( + build: { try $0.run(environment) }, + generate: { try $0.run() }, + update: { try $0.run() } + ) + } + + public static var liveValue: CliClient { + .live(environment: ProcessInfo.processInfo.environment) + } +} + +public extension CliClient { + + // TODO: Use Int for `verbose`. + struct SharedOptions: Sendable { + let dryRun: Bool + let fileName: String + let target: String + let verbose: Bool + + public init( + dryRun: Bool = false, + fileName: String, + target: String, + verbose: Bool = true + ) { + self.dryRun = dryRun + self.fileName = fileName + self.target = target + self.verbose = verbose + } + } + + struct BuildOptions: Sendable { + let gitDirectory: String? + let shared: SharedOptions + + public init( + gitDirectory: String? = nil, + shared: SharedOptions + ) { + self.gitDirectory = gitDirectory + self.shared = shared + } + } + + struct GenerateOptions: Sendable { + let shared: SharedOptions + + public init(shared: SharedOptions) { + self.shared = shared + } + } + + struct UpdateOptions: Sendable { + let gitDirectory: String? + let shared: SharedOptions + + public init( + gitDirectory: String? = nil, + shared: SharedOptions + ) { + self.gitDirectory = gitDirectory + self.shared = shared + } + } +} + +// MARK: Private + +@_spi(Internal) +public extension CliClient.SharedOptions { + var fileUrl: URL { + url(for: target).appendingPathComponent(fileName) + } + + func parseTarget() throws -> URL { + let targetUrl = fileUrl + .deletingLastPathComponent() + .deletingLastPathComponent() + + guard targetUrl.lastPathComponent == "Sources" else { + return url(for: "Sources") + .appendingPathComponent(target) + .appendingPathComponent(fileName) + } + return fileUrl + } + + @discardableResult + func run( + _ operation: () throws -> T + ) rethrows -> T { + try withDependencies { + $0.logger.logLevel = .init(verbose: verbose) + } operation: { + try operation() + } + } +} + +private extension CliClient.BuildOptions { + + func run(_ environment: [String: String]) throws -> String { + try shared.run { + @Dependency(\.gitVersionClient) var gitVersion + @Dependency(\.fileClient) var fileClient + @Dependency(\.logger) var logger + + let gitDirectory = gitDirectory ?? environment["PWD"] + + guard let gitDirectory else { + throw CliClientError.gitDirectoryNotFound + } + + logger.debug("Building with git directory: \(gitDirectory)") + + let fileUrl = shared.fileUrl + logger.debug("File url: \(fileUrl.cleanFilePath)") + + let currentVersion = try gitVersion.currentVersion(in: gitDirectory) + + let fileContents = buildTemplate + .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") + + try fileClient.write(string: fileContents, to: fileUrl) + + return fileUrl.cleanFilePath + } + } +} + +private extension CliClient.GenerateOptions { + + func run() throws -> String { + @Dependency(\.fileClient) var fileClient + @Dependency(\.logger) var logger + + let targetUrl = try shared.parseTarget() + + logger.debug("Generate target url: \(targetUrl.cleanFilePath)") + + guard !fileClient.fileExists(targetUrl) else { + throw CliClientError.fileExists(path: targetUrl.cleanFilePath) + } + + if !shared.dryRun { + try fileClient.write(string: optionalTemplate, to: targetUrl) + } else { + logger.debug("Skipping, due to dry-run being passed.") + } + return targetUrl.cleanFilePath + } +} + +private extension CliClient.UpdateOptions { + + func run() throws -> String { + @Dependency(\.fileClient) var fileClient + @Dependency(\.gitVersionClient) var gitVersionClient + @Dependency(\.logger) var logger + + let targetUrl = try shared.parseTarget() + logger.debug("Target url: \(targetUrl.cleanFilePath)") + + let currentVersion = try gitVersionClient.currentVersion(in: gitDirectory) + + let fileContents = optionalTemplate + .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") + + if !shared.dryRun { + try fileClient.write(string: fileContents, to: targetUrl) + } else { + logger.debug("Skipping due to dry run being passed.") + logger.debug("Parsed version: \(currentVersion)") + } + return targetUrl.cleanFilePath + } +} + +private let optionalTemplate = """ +// Do not set this variable, it is set during the build process. +let VERSION: String? = nil +""" + +private let buildTemplate = """ +// Do not set this variable, it is set during the build process. +let VERSION: String = nil +""" + +enum CliClientError: Error { + case gitDirectoryNotFound + case fileExists(path: String) +} diff --git a/Sources/CliVersion/FileClient.swift b/Sources/CliVersion/FileClient.swift index 2acdb78..245dc01 100644 --- a/Sources/CliVersion/FileClient.swift +++ b/Sources/CliVersion/FileClient.swift @@ -1,10 +1,21 @@ import Dependencies +import DependenciesMacros import Foundation #if canImport(FoundationNetworking) import FoundationNetworking #endif import XCTestDynamicOverlay +public extension DependencyValues { + + /// Access a basic ``FileClient`` that can read / write data to the file system. + /// + var fileClient: FileClient { + get { self[FileClient.self] } + set { self[FileClient.self] = newValue } + } +} + /// Represents the interactions with the file system. It is able /// to read from and write to files. /// @@ -13,25 +24,23 @@ import XCTestDynamicOverlay /// @Dependency(\.fileClient) var fileClient /// ``` /// -public struct FileClient { +@DependencyClient +public struct FileClient: Sendable { + + public var fileExists: @Sendable (URL) -> Bool = { _ in true } + + /// Read the contents of a file. + public var read: @Sendable (URL) throws -> String /// Write `Data` to a file `URL`. - public private(set) var write: (Data, URL) throws -> Void + public private(set) var write: @Sendable (Data, URL) throws -> Void - /// Create a new ``GitVersion/FileClient`` instance. - /// - /// This is generally not interacted with directly, instead access as a dependency. - /// - ///```swift - /// @Dependency(\.fileClient) var fileClient - ///``` + /// Read the contents of a file at the given path. /// /// - Parameters: - /// - write: Write the data to a file. - public init( - write: @escaping (Data, URL) throws -> Void - ) { - self.write = write + /// - path: The file path to read from. + public func read(_ path: String) throws -> String { + try read(url(for: path)) } /// Write's the the string to a file path. @@ -40,8 +49,8 @@ public struct FileClient { /// - string: The string to write to the file. /// - path: The file path. public func write(string: String, to path: String) throws { - let url = try url(for: path) - try self.write(string: string, to: url) + let url = url(for: path) + try write(string: string, to: url) } /// Write's the the string to a file path. @@ -50,49 +59,27 @@ public struct FileClient { /// - string: The string to write to the file. /// - url: The file url. public func write(string: String, to url: URL) throws { - try self.write(Data(string.utf8), url) + try write(Data(string.utf8), url) } } extension FileClient: DependencyKey { /// A ``FileClient`` that does not do anything. - public static let noop = FileClient.init( + public static let noop = FileClient( + fileExists: { _ in true }, + read: { _ in "" }, write: { _, _ in } ) /// An `unimplemented` ``FileClient``. - public static let testValue = FileClient( - write: unimplemented("\(Self.self).write") - ) + public static let testValue = FileClient() /// The live ``FileClient`` public static let liveValue = FileClient( + fileExists: { FileManager.default.fileExists(atPath: $0.cleanFilePath) }, + read: { try String(contentsOf: $0, encoding: .utf8) }, write: { try $0.write(to: $1, options: .atomic) } ) } - -extension DependencyValues { - - /// Access a basic ``FileClient`` that can read / write data to the file system. - /// - public var fileClient: FileClient { - get { self[FileClient.self] } - set { self[FileClient.self] = newValue } - } -} - -// MARK: - Private -fileprivate func url(for path: String) throws -> URL { - #if os(Linux) - return URL(fileURLWithPath: path) - #else - if #available(macOS 13.0, *) { - return URL(filePath: path) - } else { - // Fallback on earlier versions - return URL(fileURLWithPath: path) - } - #endif -} diff --git a/Sources/CliVersion/GitVersionClient.swift b/Sources/CliVersion/GitVersionClient.swift index 20a09a3..65ff8ae 100644 --- a/Sources/CliVersion/GitVersionClient.swift +++ b/Sources/CliVersion/GitVersionClient.swift @@ -40,7 +40,7 @@ public struct GitVersionClient { /// - Parameters: /// - gitDirectory: The directory to run the command in. public func currentVersion(in gitDirectory: String? = nil) throws -> String { - try self.currentVersion(gitDirectory) + try currentVersion(gitDirectory) } /// Override the `currentVersion` command and return the passed in version string. @@ -50,7 +50,7 @@ public struct GitVersionClient { /// - Parameters: /// - version: The version string to return when `currentVersion` is called. public mutating func override(with version: String) { - self.currentVersion = { _ in version } + currentVersion = { _ in version } } } @@ -69,32 +69,33 @@ extension GitVersionClient: TestDependencyKey { } } -extension DependencyValues { +public extension DependencyValues { /// A ``GitVersionClient`` that can retrieve the current version from a /// git directory. - public var gitVersionClient: GitVersionClient { + var gitVersionClient: GitVersionClient { get { self[GitVersionClient.self] } set { self[GitVersionClient.self] = newValue } } } -extension ShellCommand { - public static func gitCurrentSha(gitDirectory: String? = nil) -> Self { +public extension ShellCommand { + static func gitCurrentSha(gitDirectory: String? = nil) -> Self { GitVersion(workingDirectory: gitDirectory).command(for: .commit) } - public static func gitCurrentBranch(gitDirectory: String? = nil) -> Self { + static func gitCurrentBranch(gitDirectory: String? = nil) -> Self { GitVersion(workingDirectory: gitDirectory).command(for: .branch) } - public static func gitCurrentTag(gitDirectory: String? = nil) -> Self { + static func gitCurrentTag(gitDirectory: String? = nil) -> Self { GitVersion(workingDirectory: gitDirectory).command(for: .describe) } } // MARK: - Private -fileprivate struct GitVersion { + +private struct GitVersion { @Dependency(\.logger) var logger: Logger @Dependency(\.shellClient) var shell: ShellClient @@ -113,7 +114,7 @@ fileprivate struct GitVersion { } } - internal func command(for argument: VersionArgs) -> ShellCommand { + func command(for argument: VersionArgs) -> ShellCommand { .init( shell: .env, environment: nil, @@ -123,7 +124,7 @@ fileprivate struct GitVersion { } } -fileprivate extension GitVersion { +private extension GitVersion { func run(command: ShellCommand) throws -> String { try shell.background(command, trimmingCharactersIn: .whitespacesAndNewlines) } diff --git a/Sources/CliVersion/Helpers.swift b/Sources/CliVersion/Helpers.swift new file mode 100644 index 0000000..bf922d7 --- /dev/null +++ b/Sources/CliVersion/Helpers.swift @@ -0,0 +1,40 @@ +import Foundation +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif +import Logging + +@_spi(Internal) +public extension Logger.Level { + + init(verbose: Bool) { + if verbose { + self = .debug + } else { + self = .info + } + } +} + +@_spi(Internal) +public extension URL { + var cleanFilePath: String { + absoluteString.replacingOccurrences(of: "file://", with: "") + } +} + +// MARK: - Private + +@_spi(Internal) +public func url(for path: String) -> URL { + #if os(Linux) + return URL(fileURLWithPath: path) + #else + if #available(macOS 13.0, *) { + return URL(filePath: path) + } else { + // Fallback on earlier versions + return URL(fileURLWithPath: path) + } + #endif +} diff --git a/Sources/TestSupport/TestSupport.swift b/Sources/TestSupport/TestSupport.swift new file mode 100644 index 0000000..04a0380 --- /dev/null +++ b/Sources/TestSupport/TestSupport.swift @@ -0,0 +1,23 @@ +import Foundation + +// swiftlint:disable force_try + +/// Helper to create a temporary directory for running tests in. +/// +/// The temporary directory will be removed after the operation has ran. +/// +/// - Parameters: +/// - operation: The operation to run with the temporary directory. +public func withTemporaryDirectory( + _ operation: (URL) throws -> Void +) rethrows { + let tempUrl = FileManager.default + .temporaryDirectory + .appendingPathComponent(UUID().uuidString) + + try! FileManager.default.createDirectory(at: tempUrl, withIntermediateDirectories: false) + try operation(tempUrl) + try! FileManager.default.removeItem(at: tempUrl) +} + +// swiftlint:enable force_try diff --git a/Sources/cli-version/BuildCommand.swift b/Sources/cli-version/BuildCommand.swift index abc6108..880f728 100644 --- a/Sources/cli-version/BuildCommand.swift +++ b/Sources/cli-version/BuildCommand.swift @@ -1,6 +1,6 @@ import ArgumentParser -import Foundation import CliVersion +import Foundation import ShellClient extension CliVersionCommand { @@ -9,15 +9,16 @@ extension CliVersionCommand { abstract: "Used for the build with version plugin.", discussion: "This should generally not be interacted with directly, outside of the build plugin." ) - + @OptionGroup var shared: SharedOptions - + @Option( name: .customLong("git-directory"), help: "The git directory for the version." ) var gitDirectory: String - + + // TODO: Use CliClient func run() throws { try withDependencies { $0.logger.logLevel = .debug @@ -29,10 +30,10 @@ extension CliVersionCommand { @Dependency(\.logger) var logger: Logger logger.info("Building with git-directory: \(gitDirectory)") - + let fileUrl = URL(fileURLWithPath: shared.target) .appendingPathComponent(shared.fileName) - + let fileString = fileUrl.fileString() logger.info("File Url: \(fileString)") @@ -40,11 +41,10 @@ extension CliVersionCommand { let fileContents = buildTemplate .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") - + try fileClient.write(string: fileContents, to: fileUrl) logger.info("Updated version file: \(fileString)") } } } } - diff --git a/Sources/cli-version/CliVersionCommand.swift b/Sources/cli-version/CliVersionCommand.swift index 81f66b4..94f92c9 100644 --- a/Sources/cli-version/CliVersionCommand.swift +++ b/Sources/cli-version/CliVersionCommand.swift @@ -3,15 +3,13 @@ import Foundation @main struct CliVersionCommand: ParsableCommand { - - static var configuration: CommandConfiguration = .init( - commandName: "cli-version", - version: VERSION ?? "0.0.0", - subcommands: [ - Build.self, - Generate.self, - Update.self - ] - ) + static var configuration: CommandConfiguration = .init( + commandName: "cli-version", + version: VERSION ?? "0.0.0", + subcommands: [ + Build.self, + Generate.self, + Update.self, + ] + ) } - diff --git a/Sources/cli-version/GenerateCommand.swift b/Sources/cli-version/GenerateCommand.swift index 41f01c9..1b60050 100644 --- a/Sources/cli-version/GenerateCommand.swift +++ b/Sources/cli-version/GenerateCommand.swift @@ -1,11 +1,10 @@ import ArgumentParser +import CliVersion import Dependencies import Foundation -import CliVersion import ShellClient extension CliVersionCommand { - struct Generate: ParsableCommand { static var configuration: CommandConfiguration = .init( abstract: "Generates a version file in a command line tool that can be set via the git tag or git sha.", @@ -15,6 +14,7 @@ extension CliVersionCommand { @OptionGroup var shared: SharedOptions + // TODO: Use CliClient func run() throws { @Dependency(\.logger) var logger: Logger @Dependency(\.fileClient) var fileClient @@ -35,12 +35,10 @@ extension CliVersionCommand { } else { logger.info("Would generate file at: \(fileString)") } - } } } -fileprivate enum GenerationError: Error { +private enum GenerationError: Error { case fileExists(path: String) } - diff --git a/Sources/cli-version/Helpers.swift b/Sources/cli-version/Helpers.swift index da31d29..2f795c4 100644 --- a/Sources/cli-version/Helpers.swift +++ b/Sources/cli-version/Helpers.swift @@ -15,7 +15,7 @@ func parseTarget(_ target: String) -> URL { extension URL { func fileString() -> String { - self.absoluteString + absoluteString .replacingOccurrences(of: "file://", with: "") } } @@ -32,6 +32,7 @@ let VERSION: String = nil """ +// TODO: Use Int for `verbose`. struct SharedOptions: ParsableArguments { @Argument(help: "The target for the version file.") diff --git a/Sources/cli-version/UpdateCommand.swift b/Sources/cli-version/UpdateCommand.swift index a97b0e6..c22ed73 100644 --- a/Sources/cli-version/UpdateCommand.swift +++ b/Sources/cli-version/UpdateCommand.swift @@ -1,6 +1,7 @@ import ArgumentParser -import Foundation import CliVersion +import Dependencies +import Foundation import ShellClient extension CliVersionCommand { @@ -17,8 +18,9 @@ extension CliVersionCommand { name: .customLong("git-directory"), help: "The git directory for the version." ) - var gitDirectory: String? = nil - + var gitDirectory: String? + + // TODO: Use CliClient func run() throws { try withDependencies { $0.logger.logLevel = shared.verbose ? .debug : .info @@ -54,6 +56,6 @@ extension CliVersionCommand { } } -fileprivate enum UpdateError: Error { +private enum UpdateError: Error { case versionFileDoesNotExist(path: String) } diff --git a/Tests/CliVersionTests/CliVersionTests.swift b/Tests/CliVersionTests/CliVersionTests.swift index a93d0f5..0361c4d 100644 --- a/Tests/CliVersionTests/CliVersionTests.swift +++ b/Tests/CliVersionTests/CliVersionTests.swift @@ -1,6 +1,8 @@ -import XCTest -import CliVersion +@_spi(Internal) import CliVersion +import Dependencies import ShellClient +import TestSupport +import XCTest final class GitVersionTests: XCTestCase { @@ -21,8 +23,7 @@ final class GitVersionTests: XCTestCase { .deletingLastPathComponent() .deletingLastPathComponent() .deletingLastPathComponent() - .absoluteString - .replacingOccurrences(of: "file://", with: "") + .cleanFilePath } func test_overrides_work() throws { @@ -43,7 +44,6 @@ final class GitVersionTests: XCTestCase { print("VERSION: \(version)") // can't really have a predictable result for the live client. XCTAssertNotEqual(version, "blob") - } func test_commands() throws { @@ -65,38 +65,31 @@ final class GitVersionTests: XCTestCase { } func test_file_client() throws { - @Dependency(\.fileClient) var fileClient - - let tmpDir = FileManager.default.temporaryDirectory - .appendingPathComponent("file-client-test") - - try FileManager.default.createDirectory(at: tmpDir, withIntermediateDirectories: true) - defer { try! FileManager.default.removeItem(at: tmpDir) } - - let filePath = tmpDir.appendingPathComponent("blob.txt") - try fileClient.write(string: "Blob", to: filePath) - - let contents = try String(contentsOf: filePath) - .trimmingCharacters(in: .whitespacesAndNewlines) - XCTAssertEqual(contents, "Blob") + try withTemporaryDirectory { tmpDir in + @Dependency(\.fileClient) var fileClient + let filePath = tmpDir.appendingPathComponent("blob.txt") + try fileClient.write(string: "Blob", to: filePath) + + let contents = try fileClient.read(filePath) + .trimmingCharacters(in: .whitespacesAndNewlines) + XCTAssertEqual(contents, "Blob") + } } func test_file_client_with_string_path() throws { - @Dependency(\.fileClient) var fileClient + try withTemporaryDirectory { tmpDir in + @Dependency(\.fileClient) var fileClient - let tmpDir = FileManager.default.temporaryDirectory - .appendingPathComponent("file-client-string-test") + let filePath = tmpDir.appendingPathComponent("blob.txt") + let fileString = filePath.cleanFilePath - try FileManager.default.createDirectory(at: tmpDir, withIntermediateDirectories: true) - defer { try! FileManager.default.removeItem(at: tmpDir) } + try fileClient.write(string: "Blob", to: fileString) - let filePath = tmpDir.appendingPathComponent("blob.txt") - let fileString = filePath.absoluteString.replacingOccurrences(of: "file://", with: "") - try fileClient.write(string: "Blob", to: fileString) + let contents = try fileClient.read(fileString) + .trimmingCharacters(in: .whitespacesAndNewlines) - let contents = try String(contentsOf: filePath) - .trimmingCharacters(in: .whitespacesAndNewlines) - XCTAssertEqual(contents, "Blob") + XCTAssertEqual(contents, "Blob") + } } }