feat: Begins update for more modern swift-dependencies implementation.

This commit is contained in:
2024-12-20 12:54:10 -05:00
parent 1885a90f62
commit 847ddbc7b5
19 changed files with 439 additions and 130 deletions

7
.editorconfig Normal file
View File

@@ -0,0 +1,7 @@
root = true
[*.swift]
indent_style = space
indent_size = 2
tab_width = 2
trim_trailing_whitespace = true

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ DerivedData/
.swiftpm/config/registries.json .swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc .netrc
.nvim/*

11
.swiftformat Normal file
View File

@@ -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

11
.swiftlint.yml Normal file
View File

@@ -0,0 +1,11 @@
disabled_rules:
- closing_brace
- fuction_body_length
- opening_brace
- nesting
included:
- Sources
- Tests
ignore_multiline_statement_conditions: true

View File

@@ -96,7 +96,8 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables> <Testables>
<TestableReference <TestableReference
skipped = "NO"> skipped = "NO">

View File

@@ -5,8 +5,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/combine-schedulers", "location" : "https://github.com/pointfreeco/combine-schedulers",
"state" : { "state" : {
"revision" : "9dc9cbe4bc45c65164fa653a563d8d8db61b09bb", "revision" : "9fa31f4403da54855f1e2aeaeff478f4f0e40b13",
"version" : "1.0.0" "version" : "1.0.2"
} }
}, },
{ {
@@ -32,8 +32,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-clocks", "location" : "https://github.com/pointfreeco/swift-clocks",
"state" : { "state" : {
"revision" : "d1fd837326aa719bee979bdde1f53cd5797443eb", "revision" : "b9b24b69e2adda099a1fa381cda1eeec272d5b53",
"version" : "1.0.0" "version" : "1.0.5"
} }
}, },
{ {
@@ -50,8 +50,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-dependencies.git", "location" : "https://github.com/pointfreeco/swift-dependencies.git",
"state" : { "state" : {
"revision" : "4e1eb6e28afe723286d8cc60611237ffbddba7c5", "revision" : "5526c8a27675dc7b18d6fa643abfb64bcb200b77",
"version" : "1.0.0" "version" : "1.6.2"
} }
}, },
{ {
@@ -99,13 +99,22 @@
"version" : "0.1.4" "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", "identity" : "xctest-dynamic-overlay",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state" : { "state" : {
"revision" : "23cbf2294e350076ea4dbd7d5d047c1e76b03631", "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1",
"version" : "1.0.2" "version" : "1.4.3"
} }
} }
], ],

View File

@@ -1,4 +1,4 @@
// swift-tools-version: 5.6 // swift-tools-version: 5.10
import PackageDescription import PackageDescription
@@ -14,6 +14,7 @@ let package = Package(
.plugin(name: "UpdateVersionPlugin", targets: ["UpdateVersionPlugin"]) .plugin(name: "UpdateVersionPlugin", targets: ["UpdateVersionPlugin"])
], ],
dependencies: [ 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/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-docc-plugin.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2") .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") .product(name: "ArgumentParser", package: "swift-argument-parser")
] ]
), ),
.target(name: "TestSupport"),
.target( .target(
name: "CliVersion", name: "CliVersion",
dependencies: [ dependencies: [
.product(name: "Dependencies", package: "swift-dependencies"),
.product(name: "DependenciesMacros", package: "swift-dependencies"),
.product(name: "ShellClient", package: "swift-shell-client") .product(name: "ShellClient", package: "swift-shell-client")
] ]
), ),
.testTarget( .testTarget(
name: "CliVersionTests", name: "CliVersionTests",
dependencies: ["CliVersion"] dependencies: ["CliVersion", "TestSupport"]
), ),
.plugin( .plugin(
name: "BuildWithVersionPlugin", name: "BuildWithVersionPlugin",

View File

@@ -8,16 +8,16 @@ struct GenerateVersionBuildPlugin: BuildToolPlugin {
target: PackagePlugin.Target target: PackagePlugin.Target
) async throws -> [PackagePlugin.Command] { ) async throws -> [PackagePlugin.Command] {
guard let target = target as? SourceModuleTarget else { return [] } guard let target = target as? SourceModuleTarget else { return [] }
let gitDirectoryPath = target.directory let gitDirectoryPath = target.directory
.removingLastComponent() .removingLastComponent()
.removingLastComponent() .removingLastComponent()
let tool = try context.tool(named: "cli-version") let tool = try context.tool(named: "cli-version")
let outputPath = context.pluginWorkDirectory let outputPath = context.pluginWorkDirectory
let outputFile = outputPath.appending("Version.swift") let outputFile = outputPath.appending("Version.swift")
return [ return [
.buildCommand( .buildCommand(
displayName: "Build With Version Plugin", displayName: "Build With Version Plugin",

View File

@@ -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<T>(
_ 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)
}

View File

@@ -1,10 +1,21 @@
import Dependencies import Dependencies
import DependenciesMacros
import Foundation import Foundation
#if canImport(FoundationNetworking) #if canImport(FoundationNetworking)
import FoundationNetworking import FoundationNetworking
#endif #endif
import XCTestDynamicOverlay 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 /// Represents the interactions with the file system. It is able
/// to read from and write to files. /// to read from and write to files.
/// ///
@@ -13,25 +24,23 @@ import XCTestDynamicOverlay
/// @Dependency(\.fileClient) var fileClient /// @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`. /// 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. /// Read the contents of a file at the given path.
///
/// This is generally not interacted with directly, instead access as a dependency.
///
///```swift
/// @Dependency(\.fileClient) var fileClient
///```
/// ///
/// - Parameters: /// - Parameters:
/// - write: Write the data to a file. /// - path: The file path to read from.
public init( public func read(_ path: String) throws -> String {
write: @escaping (Data, URL) throws -> Void try read(url(for: path))
) {
self.write = write
} }
/// Write's the the string to a file 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. /// - string: The string to write to the file.
/// - path: The file path. /// - path: The file path.
public func write(string: String, to path: String) throws { public func write(string: String, to path: String) throws {
let url = try url(for: path) let url = url(for: path)
try self.write(string: string, to: url) try write(string: string, to: url)
} }
/// Write's the the string to a file path. /// Write's the the string to a file path.
@@ -50,49 +59,27 @@ public struct FileClient {
/// - string: The string to write to the file. /// - string: The string to write to the file.
/// - url: The file url. /// - url: The file url.
public func write(string: String, to url: URL) throws { 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 { extension FileClient: DependencyKey {
/// A ``FileClient`` that does not do anything. /// 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 } write: { _, _ in }
) )
/// An `unimplemented` ``FileClient``. /// An `unimplemented` ``FileClient``.
public static let testValue = FileClient( public static let testValue = FileClient()
write: unimplemented("\(Self.self).write")
)
/// The live ``FileClient`` /// The live ``FileClient``
public static let liveValue = 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) } 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
}

View File

@@ -40,7 +40,7 @@ public struct GitVersionClient {
/// - Parameters: /// - Parameters:
/// - gitDirectory: The directory to run the command in. /// - gitDirectory: The directory to run the command in.
public func currentVersion(in gitDirectory: String? = nil) throws -> String { 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. /// Override the `currentVersion` command and return the passed in version string.
@@ -50,7 +50,7 @@ public struct GitVersionClient {
/// - Parameters: /// - Parameters:
/// - version: The version string to return when `currentVersion` is called. /// - version: The version string to return when `currentVersion` is called.
public mutating func override(with version: String) { 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 /// A ``GitVersionClient`` that can retrieve the current version from a
/// git directory. /// git directory.
public var gitVersionClient: GitVersionClient { var gitVersionClient: GitVersionClient {
get { self[GitVersionClient.self] } get { self[GitVersionClient.self] }
set { self[GitVersionClient.self] = newValue } set { self[GitVersionClient.self] = newValue }
} }
} }
extension ShellCommand { public extension ShellCommand {
public static func gitCurrentSha(gitDirectory: String? = nil) -> Self { static func gitCurrentSha(gitDirectory: String? = nil) -> Self {
GitVersion(workingDirectory: gitDirectory).command(for: .commit) 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) 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) GitVersion(workingDirectory: gitDirectory).command(for: .describe)
} }
} }
// MARK: - Private // MARK: - Private
fileprivate struct GitVersion {
private struct GitVersion {
@Dependency(\.logger) var logger: Logger @Dependency(\.logger) var logger: Logger
@Dependency(\.shellClient) var shell: ShellClient @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( .init(
shell: .env, shell: .env,
environment: nil, environment: nil,
@@ -123,7 +124,7 @@ fileprivate struct GitVersion {
} }
} }
fileprivate extension GitVersion { private extension GitVersion {
func run(command: ShellCommand) throws -> String { func run(command: ShellCommand) throws -> String {
try shell.background(command, trimmingCharactersIn: .whitespacesAndNewlines) try shell.background(command, trimmingCharactersIn: .whitespacesAndNewlines)
} }

View File

@@ -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
}

View File

@@ -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

View File

@@ -1,6 +1,6 @@
import ArgumentParser import ArgumentParser
import Foundation
import CliVersion import CliVersion
import Foundation
import ShellClient import ShellClient
extension CliVersionCommand { extension CliVersionCommand {
@@ -9,15 +9,16 @@ extension CliVersionCommand {
abstract: "Used for the build with version plugin.", abstract: "Used for the build with version plugin.",
discussion: "This should generally not be interacted with directly, outside of the build plugin." discussion: "This should generally not be interacted with directly, outside of the build plugin."
) )
@OptionGroup var shared: SharedOptions @OptionGroup var shared: SharedOptions
@Option( @Option(
name: .customLong("git-directory"), name: .customLong("git-directory"),
help: "The git directory for the version." help: "The git directory for the version."
) )
var gitDirectory: String var gitDirectory: String
// TODO: Use CliClient
func run() throws { func run() throws {
try withDependencies { try withDependencies {
$0.logger.logLevel = .debug $0.logger.logLevel = .debug
@@ -29,10 +30,10 @@ extension CliVersionCommand {
@Dependency(\.logger) var logger: Logger @Dependency(\.logger) var logger: Logger
logger.info("Building with git-directory: \(gitDirectory)") logger.info("Building with git-directory: \(gitDirectory)")
let fileUrl = URL(fileURLWithPath: shared.target) let fileUrl = URL(fileURLWithPath: shared.target)
.appendingPathComponent(shared.fileName) .appendingPathComponent(shared.fileName)
let fileString = fileUrl.fileString() let fileString = fileUrl.fileString()
logger.info("File Url: \(fileString)") logger.info("File Url: \(fileString)")
@@ -40,11 +41,10 @@ extension CliVersionCommand {
let fileContents = buildTemplate let fileContents = buildTemplate
.replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"")
try fileClient.write(string: fileContents, to: fileUrl) try fileClient.write(string: fileContents, to: fileUrl)
logger.info("Updated version file: \(fileString)") logger.info("Updated version file: \(fileString)")
} }
} }
} }
} }

View File

@@ -3,15 +3,13 @@ import Foundation
@main @main
struct CliVersionCommand: ParsableCommand { struct CliVersionCommand: ParsableCommand {
static var configuration: CommandConfiguration = .init(
static var configuration: CommandConfiguration = .init( commandName: "cli-version",
commandName: "cli-version", version: VERSION ?? "0.0.0",
version: VERSION ?? "0.0.0", subcommands: [
subcommands: [ Build.self,
Build.self, Generate.self,
Generate.self, Update.self,
Update.self ]
] )
)
} }

View File

@@ -1,11 +1,10 @@
import ArgumentParser import ArgumentParser
import CliVersion
import Dependencies import Dependencies
import Foundation import Foundation
import CliVersion
import ShellClient import ShellClient
extension CliVersionCommand { extension CliVersionCommand {
struct Generate: ParsableCommand { struct Generate: ParsableCommand {
static var configuration: CommandConfiguration = .init( 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.", 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 @OptionGroup var shared: SharedOptions
// TODO: Use CliClient
func run() throws { func run() throws {
@Dependency(\.logger) var logger: Logger @Dependency(\.logger) var logger: Logger
@Dependency(\.fileClient) var fileClient @Dependency(\.fileClient) var fileClient
@@ -35,12 +35,10 @@ extension CliVersionCommand {
} else { } else {
logger.info("Would generate file at: \(fileString)") logger.info("Would generate file at: \(fileString)")
} }
} }
} }
} }
fileprivate enum GenerationError: Error { private enum GenerationError: Error {
case fileExists(path: String) case fileExists(path: String)
} }

View File

@@ -15,7 +15,7 @@ func parseTarget(_ target: String) -> URL {
extension URL { extension URL {
func fileString() -> String { func fileString() -> String {
self.absoluteString absoluteString
.replacingOccurrences(of: "file://", with: "") .replacingOccurrences(of: "file://", with: "")
} }
} }
@@ -32,6 +32,7 @@ let VERSION: String = nil
""" """
// TODO: Use Int for `verbose`.
struct SharedOptions: ParsableArguments { struct SharedOptions: ParsableArguments {
@Argument(help: "The target for the version file.") @Argument(help: "The target for the version file.")

View File

@@ -1,6 +1,7 @@
import ArgumentParser import ArgumentParser
import Foundation
import CliVersion import CliVersion
import Dependencies
import Foundation
import ShellClient import ShellClient
extension CliVersionCommand { extension CliVersionCommand {
@@ -17,8 +18,9 @@ extension CliVersionCommand {
name: .customLong("git-directory"), name: .customLong("git-directory"),
help: "The git directory for the version." help: "The git directory for the version."
) )
var gitDirectory: String? = nil var gitDirectory: String?
// TODO: Use CliClient
func run() throws { func run() throws {
try withDependencies { try withDependencies {
$0.logger.logLevel = shared.verbose ? .debug : .info $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) case versionFileDoesNotExist(path: String)
} }

View File

@@ -1,6 +1,8 @@
import XCTest @_spi(Internal) import CliVersion
import CliVersion import Dependencies
import ShellClient import ShellClient
import TestSupport
import XCTest
final class GitVersionTests: XCTestCase { final class GitVersionTests: XCTestCase {
@@ -21,8 +23,7 @@ final class GitVersionTests: XCTestCase {
.deletingLastPathComponent() .deletingLastPathComponent()
.deletingLastPathComponent() .deletingLastPathComponent()
.deletingLastPathComponent() .deletingLastPathComponent()
.absoluteString .cleanFilePath
.replacingOccurrences(of: "file://", with: "")
} }
func test_overrides_work() throws { func test_overrides_work() throws {
@@ -43,7 +44,6 @@ final class GitVersionTests: XCTestCase {
print("VERSION: \(version)") print("VERSION: \(version)")
// can't really have a predictable result for the live client. // can't really have a predictable result for the live client.
XCTAssertNotEqual(version, "blob") XCTAssertNotEqual(version, "blob")
} }
func test_commands() throws { func test_commands() throws {
@@ -65,38 +65,31 @@ final class GitVersionTests: XCTestCase {
} }
func test_file_client() throws { func test_file_client() throws {
@Dependency(\.fileClient) var fileClient try withTemporaryDirectory { tmpDir in
@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")
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 { 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 let filePath = tmpDir.appendingPathComponent("blob.txt")
.appendingPathComponent("file-client-string-test") let fileString = filePath.cleanFilePath
try FileManager.default.createDirectory(at: tmpDir, withIntermediateDirectories: true) try fileClient.write(string: "Blob", to: fileString)
defer { try! FileManager.default.removeItem(at: tmpDir) }
let filePath = tmpDir.appendingPathComponent("blob.txt") let contents = try fileClient.read(fileString)
let fileString = filePath.absoluteString.replacingOccurrences(of: "file://", with: "") .trimmingCharacters(in: .whitespacesAndNewlines)
try fileClient.write(string: "Blob", to: fileString)
let contents = try String(contentsOf: filePath) XCTAssertEqual(contents, "Blob")
.trimmingCharacters(in: .whitespacesAndNewlines) }
XCTAssertEqual(contents, "Blob")
} }
} }