feat: Updates dependencies and cli commands to be async.

This commit is contained in:
2024-12-21 00:07:01 -05:00
parent b7ac6ee9d1
commit 59bc977788
10 changed files with 185 additions and 195 deletions

View File

@@ -21,6 +21,7 @@ public struct CliClient: Sendable {
/// Build and update the version based on the git tag, or branch + sha. /// Build and update the version based on the git tag, or branch + sha.
public var build: @Sendable (SharedOptions) async throws -> String public var build: @Sendable (SharedOptions) async throws -> String
/// Bump the existing version.
public var bump: @Sendable (BumpOption, SharedOptions) async throws -> String public var bump: @Sendable (BumpOption, SharedOptions) async throws -> String
/// Generate a version file with an optional version that can be set manually. /// Generate a version file with an optional version that can be set manually.
@@ -63,10 +64,10 @@ extension CliClient: DependencyKey {
public static func live(environment: [String: String]) -> Self { public static func live(environment: [String: String]) -> Self {
.init( .init(
build: { try $0.build(environment) }, build: { try await $0.build(environment) },
bump: { try $1.bump($0) }, bump: { try await $1.bump($0) },
generate: { try $0.generate() }, generate: { try await $0.generate() },
update: { try $0.update() } update: { try await $0.update() }
) )
} }
@@ -97,20 +98,20 @@ public extension CliClient.SharedOptions {
@discardableResult @discardableResult
func run<T>( func run<T>(
_ operation: () throws -> T _ operation: () async throws -> T
) rethrows -> T { ) async rethrows -> T {
try withDependencies { try await withDependencies {
$0.logger.logLevel = .init(verbose: verbose) $0.logger.logLevel = .init(verbose: verbose)
} operation: { } operation: {
try operation() try await operation()
} }
} }
} }
private extension CliClient.SharedOptions { private extension CliClient.SharedOptions {
func build(_ environment: [String: String]) throws -> String { func build(_ environment: [String: String]) async throws -> String {
try run { try await run {
@Dependency(\.gitVersionClient) var gitVersion @Dependency(\.gitVersionClient) var gitVersion
@Dependency(\.fileClient) var fileClient @Dependency(\.fileClient) var fileClient
@Dependency(\.logger) var logger @Dependency(\.logger) var logger
@@ -126,91 +127,95 @@ private extension CliClient.SharedOptions {
let fileUrl = self.fileUrl let fileUrl = self.fileUrl
logger.debug("File url: \(fileUrl.cleanFilePath)") logger.debug("File url: \(fileUrl.cleanFilePath)")
let currentVersion = try gitVersion.currentVersion(in: gitDirectory) let currentVersion = try await gitVersion.currentVersion(in: gitDirectory)
let fileContents = Template.build(currentVersion) let fileContents = Template.build(currentVersion)
try fileClient.write(string: fileContents, to: fileUrl) try await fileClient.write(string: fileContents, to: fileUrl)
return fileUrl.cleanFilePath return fileUrl.cleanFilePath
} }
} }
func bump(_ type: CliClient.BumpOption) throws -> String { func bump(_ type: CliClient.BumpOption) async throws -> String {
@Dependency(\.fileClient) var fileClient try await run {
@Dependency(\.logger) var logger @Dependency(\.fileClient) var fileClient
@Dependency(\.logger) var logger
let targetUrl = fileUrl let targetUrl = fileUrl
logger.debug("Bump target url: \(targetUrl.cleanFilePath)") logger.debug("Bump target url: \(targetUrl.cleanFilePath)")
let contents = try fileClient.read(fileUrl.cleanFilePath) let contents = try await fileClient.read(fileUrl)
let versionLine = contents.split(separator: "\n") let versionLine = contents.split(separator: "\n")
.first { $0.hasPrefix("let VERSION:") } .first { $0.hasPrefix("let VERSION:") }
guard let versionLine else { guard let versionLine else {
throw CliClientError.failedToParseVersionFile throw CliClientError.failedToParseVersionFile
}
let isOptional = versionLine.contains("String?")
let versionString = versionLine.split(separator: "let VERSION: \(isOptional ? "String?" : "String") = ").last
guard let versionString else {
throw CliClientError.failedToParseVersionFile
}
let parts = String(versionString).split(separator: ".")
logger.debug("Version parts: \(parts)")
// TODO: Better error.
guard parts.count == 3 else {
throw CliClientError.failedToParseVersionFile
}
var major = Int(String(parts[0])) ?? 0
var minor = Int(String(parts[1])) ?? 0
var patch = Int(String(parts[2])) ?? 0
type.bump(major: &major, minor: &minor, patch: &patch)
let version = "\(major).\(minor).\(patch)"
logger.debug("Bumped version: \(version)")
let template = isOptional ? Template.optional(version) : Template.build(version)
if !dryRun {
try await fileClient.write(string: template, to: targetUrl)
} else {
logger.debug("Skipping, due to dry-run being passed.")
}
return targetUrl.cleanFilePath
} }
let isOptional = versionLine.contains("String?")
let versionString = versionLine.split(separator: "let VERSION: \(isOptional ? "String?" : "String") = ").last
guard let versionString else {
throw CliClientError.failedToParseVersionFile
}
let parts = String(versionString).split(separator: ".")
logger.debug("Version parts: \(parts)")
// TODO: Better error.
guard parts.count == 3 else {
throw CliClientError.failedToParseVersionFile
}
var major = Int(String(parts[0])) ?? 0
var minor = Int(String(parts[1])) ?? 0
var patch = Int(String(parts[2])) ?? 0
type.bump(major: &major, minor: &minor, patch: &patch)
let version = "\(major).\(minor).\(patch)"
logger.debug("Bumped version: \(version)")
let template = isOptional ? Template.optional(version) : Template.build(version)
if !dryRun {
try fileClient.write(string: template, to: targetUrl)
} else {
logger.debug("Skipping, due to dry-run being passed.")
}
return targetUrl.cleanFilePath
} }
func generate(_ version: String? = nil) throws -> String { func generate(_ version: String? = nil) async throws -> String {
@Dependency(\.fileClient) var fileClient try await run {
@Dependency(\.logger) var logger @Dependency(\.fileClient) var fileClient
@Dependency(\.logger) var logger
let targetUrl = fileUrl let targetUrl = fileUrl
logger.debug("Generate target url: \(targetUrl.cleanFilePath)") logger.debug("Generate target url: \(targetUrl.cleanFilePath)")
guard !fileClient.fileExists(targetUrl) else { guard !fileClient.fileExists(targetUrl) else {
throw CliClientError.fileExists(path: targetUrl.cleanFilePath) throw CliClientError.fileExists(path: targetUrl.cleanFilePath)
}
let template = Template.optional(version)
if !dryRun {
try await fileClient.write(string: template, to: targetUrl)
} else {
logger.debug("Skipping, due to dry-run being passed.")
}
return targetUrl.cleanFilePath
} }
let template = Template.optional(version)
if !dryRun {
try fileClient.write(string: template, to: targetUrl)
} else {
logger.debug("Skipping, due to dry-run being passed.")
}
return targetUrl.cleanFilePath
} }
func update() throws -> String { func update() async throws -> String {
@Dependency(\.gitVersionClient) var gitVersionClient @Dependency(\.gitVersionClient) var gitVersionClient
return try generate(gitVersionClient.currentVersion(in: gitDirectory)) return try await generate(gitVersionClient.currentVersion(in: gitDirectory))
} }
} }

View File

@@ -6,6 +6,8 @@ import Foundation
#endif #endif
import XCTestDynamicOverlay import XCTestDynamicOverlay
// TODO: Need a capturing version on write for tests.
public extension DependencyValues { public extension DependencyValues {
/// Access a basic ``FileClient`` that can read / write data to the file system. /// Access a basic ``FileClient`` that can read / write data to the file system.
@@ -27,20 +29,21 @@ public extension DependencyValues {
@DependencyClient @DependencyClient
public struct FileClient: Sendable { public struct FileClient: Sendable {
/// Check if a file exists at the given url.
public var fileExists: @Sendable (URL) -> Bool = { _ in true } public var fileExists: @Sendable (URL) -> Bool = { _ in true }
/// Read the contents of a file. /// Read the contents of a file.
public var read: @Sendable (URL) throws -> String public var read: @Sendable (URL) async throws -> String
/// Write `Data` to a file `URL`. /// Write `Data` to a file `URL`.
public var write: @Sendable (Data, URL) throws -> Void public var write: @Sendable (Data, URL) async throws -> Void
/// Read the contents of a file at the given path. /// Read the contents of a file at the given path.
/// ///
/// - Parameters: /// - Parameters:
/// - path: The file path to read from. /// - path: The file path to read from.
public func read(_ path: String) throws -> String { public func read(_ path: String) async throws -> String {
try read(url(for: path)) try await read(url(for: path))
} }
/// Write's the the string to a file path. /// Write's the the string to a file path.
@@ -48,9 +51,8 @@ public struct FileClient: Sendable {
/// - Parameters: /// - Parameters:
/// - 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) async throws {
let url = url(for: path) try await write(string: string, to: url(for: path))
try write(string: string, to: url)
} }
/// Write's the the string to a file path. /// Write's the the string to a file path.
@@ -58,8 +60,8 @@ public struct FileClient: Sendable {
/// - Parameters: /// - Parameters:
/// - 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) async throws {
try write(Data(string.utf8), url) try await write(Data(string.utf8), url)
} }
} }
@@ -83,3 +85,15 @@ extension FileClient: DependencyKey {
) )
} }
private actor CapturingWrite {
var data: Data?
var url: URL?
init() {}
func set(data: Data, url: URL) {
self.data = data
self.url = url
}
}

View File

@@ -2,6 +2,8 @@ import Foundation
#if canImport(FoundationNetworking) #if canImport(FoundationNetworking)
import FoundationNetworking import FoundationNetworking
#endif #endif
import Dependencies
import DependenciesMacros
import ShellClient import ShellClient
import XCTestDynamicOverlay import XCTestDynamicOverlay
@@ -14,57 +16,32 @@ import XCTestDynamicOverlay
/// that is supplied with this library. The use case is to set the version of a command line /// that is supplied with this library. The use case is to set the version of a command line
/// tool based on the current git tag. /// tool based on the current git tag.
/// ///
public struct GitVersionClient { @DependencyClient
public struct GitVersionClient: Sendable {
/// The closure to run that returns the current version from a given /// The closure to run that returns the current version from a given
/// git directory. /// git directory.
private var currentVersion: (String?) throws -> String public var currentVersion: @Sendable (String?) async throws -> String
/// Create a new ``GitVersionClient`` instance.
///
/// This is normally not interacted with directly, instead access the client through the dependency system.
/// ```swift
/// @Dependency(\.gitVersionClient)
/// ```
///
/// - Parameters:
/// - currentVersion: The closure that returns the current version.
///
public init(currentVersion: @escaping (String?) throws -> String) {
self.currentVersion = currentVersion
}
/// Get the current version from the `git tag` in the given directory. /// Get the current version from the `git tag` in the given directory.
/// If a directory is not passed in, then we will use the current working directory. /// If a directory is not passed in, then we will use the current working directory.
/// ///
/// - 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) async throws -> String {
try currentVersion(gitDirectory) try await currentVersion(gitDirectory)
}
/// Override the `currentVersion` command and return the passed in version string.
///
/// This is useful for testing purposes.
///
/// - Parameters:
/// - version: The version string to return when `currentVersion` is called.
public mutating func override(with version: String) {
currentVersion = { _ in version }
} }
} }
extension GitVersionClient: TestDependencyKey { extension GitVersionClient: TestDependencyKey {
/// The ``GitVersionClient`` used in test / debug builds. /// The ``GitVersionClient`` used in test / debug builds.
public static let testValue = GitVersionClient( public static let testValue = GitVersionClient()
currentVersion: unimplemented("\(Self.self).currentVersion", placeholder: "")
)
/// The ``GitVersionClient`` used in release builds. /// The ``GitVersionClient`` used in release builds.
public static var liveValue: GitVersionClient { public static var liveValue: GitVersionClient {
.init(currentVersion: { gitDirectory in .init(currentVersion: { gitDirectory in
try GitVersion(workingDirectory: gitDirectory).currentVersion() try await GitVersion(workingDirectory: gitDirectory).currentVersion()
}) })
} }
} }
@@ -97,19 +74,19 @@ public extension ShellCommand {
private struct GitVersion { private struct GitVersion {
@Dependency(\.logger) var logger: Logger @Dependency(\.logger) var logger: Logger
@Dependency(\.shellClient) var shell: ShellClient @Dependency(\.asyncShellClient) var shell
let workingDirectory: String? let workingDirectory: String?
func currentVersion() throws -> String { func currentVersion() async throws -> String {
logger.debug("\("Fetching current version".bold)") logger.debug("\("Fetching current version".bold)")
do { do {
logger.debug("Checking for tag.") logger.debug("Checking for tag.")
return try run(command: command(for: .describe)) return try await run(command: command(for: .describe))
} catch { } catch {
logger.debug("\("No tag found, deferring to branch & git sha".red)") logger.debug("\("No tag found, deferring to branch & git sha".red)")
let branch = try run(command: command(for: .branch)) let branch = try await run(command: command(for: .branch))
let commit = try run(command: command(for: .commit)) let commit = try await run(command: command(for: .commit))
return "\(branch) \(commit)" return "\(branch) \(commit)"
} }
} }
@@ -125,8 +102,8 @@ private struct GitVersion {
} }
private extension GitVersion { private extension GitVersion {
func run(command: ShellCommand) throws -> String { func run(command: ShellCommand) async throws -> String {
try shell.background(command, trimmingCharactersIn: .whitespacesAndNewlines) try await shell.background(command, trimmingCharactersIn: .whitespacesAndNewlines)
} }
enum VersionArgs { enum VersionArgs {

View File

@@ -9,14 +9,14 @@ import Foundation
/// - Parameters: /// - Parameters:
/// - operation: The operation to run with the temporary directory. /// - operation: The operation to run with the temporary directory.
public func withTemporaryDirectory( public func withTemporaryDirectory(
_ operation: (URL) throws -> Void _ operation: (URL) async throws -> Void
) rethrows { ) async rethrows {
let tempUrl = FileManager.default let tempUrl = FileManager.default
.temporaryDirectory .temporaryDirectory
.appendingPathComponent(UUID().uuidString) .appendingPathComponent(UUID().uuidString)
try! FileManager.default.createDirectory(at: tempUrl, withIntermediateDirectories: false) try! FileManager.default.createDirectory(at: tempUrl, withIntermediateDirectories: false)
try operation(tempUrl) try await operation(tempUrl)
try! FileManager.default.removeItem(at: tempUrl) try! FileManager.default.removeItem(at: tempUrl)
} }

View File

@@ -4,7 +4,7 @@ import Foundation
import ShellClient import ShellClient
extension CliVersionCommand { extension CliVersionCommand {
struct Build: ParsableCommand { struct Build: AsyncParsableCommand {
static var configuration: CommandConfiguration = .init( static var configuration: CommandConfiguration = .init(
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."
@@ -19,8 +19,8 @@ extension CliVersionCommand {
var gitDirectory: String var gitDirectory: String
// TODO: Use CliClient // TODO: Use CliClient
func run() throws { func run() async throws {
try withDependencies { try await withDependencies {
$0.logger.logLevel = .debug $0.logger.logLevel = .debug
$0.fileClient = .liveValue $0.fileClient = .liveValue
$0.gitVersionClient = .liveValue $0.gitVersionClient = .liveValue
@@ -37,12 +37,12 @@ extension CliVersionCommand {
let fileString = fileUrl.fileString() let fileString = fileUrl.fileString()
logger.info("File Url: \(fileString)") logger.info("File Url: \(fileString)")
let currentVersion = try gitVersion.currentVersion(in: gitDirectory) let currentVersion = try await gitVersion.currentVersion(in: gitDirectory)
let fileContents = buildTemplate let fileContents = buildTemplate
.replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"")
try fileClient.write(string: fileContents, to: fileUrl) try await fileClient.write(string: fileContents, to: fileUrl)
logger.info("Updated version file: \(fileString)") logger.info("Updated version file: \(fileString)")
} }
} }

View File

@@ -2,14 +2,14 @@ import ArgumentParser
import Foundation import Foundation
@main @main
struct CliVersionCommand: ParsableCommand { struct CliVersionCommand: AsyncParsableCommand {
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

@@ -5,7 +5,7 @@ import Foundation
import ShellClient import ShellClient
extension CliVersionCommand { extension CliVersionCommand {
struct Generate: ParsableCommand { struct Generate: AsyncParsableCommand {
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.",
discussion: "This command can be interacted with directly, outside of the plugin usage context.", discussion: "This command can be interacted with directly, outside of the plugin usage context.",
@@ -15,7 +15,7 @@ extension CliVersionCommand {
@OptionGroup var shared: SharedOptions @OptionGroup var shared: SharedOptions
// TODO: Use CliClient // TODO: Use CliClient
func run() throws { func run() async throws {
@Dependency(\.logger) var logger: Logger @Dependency(\.logger) var logger: Logger
@Dependency(\.fileClient) var fileClient @Dependency(\.fileClient) var fileClient
@@ -30,7 +30,7 @@ extension CliVersionCommand {
} }
if !shared.dryRun { if !shared.dryRun {
try fileClient.write(string: optionalTemplate, to: fileUrl) try await fileClient.write(string: optionalTemplate, to: fileUrl)
logger.info("Generated file at: \(fileString)") logger.info("Generated file at: \(fileString)")
} else { } else {
logger.info("Would generate file at: \(fileString)") logger.info("Would generate file at: \(fileString)")

View File

@@ -6,7 +6,7 @@ import ShellClient
extension CliVersionCommand { extension CliVersionCommand {
struct Update: ParsableCommand { struct Update: AsyncParsableCommand {
static var configuration: CommandConfiguration = .init( static var configuration: CommandConfiguration = .init(
abstract: "Updates a version string to the git tag or git sha.", abstract: "Updates a version string to the git tag or git sha.",
discussion: "This command can be interacted with directly outside of the plugin context." discussion: "This command can be interacted with directly outside of the plugin context."
@@ -21,17 +21,17 @@ extension CliVersionCommand {
var gitDirectory: String? var gitDirectory: String?
// TODO: Use CliClient // TODO: Use CliClient
func run() throws { func run() async throws {
try withDependencies { try await withDependencies {
$0.logger.logLevel = shared.verbose ? .debug : .info $0.logger.logLevel = shared.verbose ? .debug : .info
$0.fileClient = .liveValue $0.fileClient = .liveValue
$0.gitVersionClient = .liveValue $0.gitVersionClient = .liveValue
$0.shellClient = .liveValue $0.asyncShellClient = .liveValue
} operation: { } operation: {
@Dependency(\.gitVersionClient) var gitVersion @Dependency(\.gitVersionClient) var gitVersion
@Dependency(\.fileClient) var fileClient @Dependency(\.fileClient) var fileClient
@Dependency(\.logger) var logger @Dependency(\.logger) var logger
@Dependency(\.shellClient) var shell @Dependency(\.asyncShellClient) var shell
let targetUrl = parseTarget(shared.target) let targetUrl = parseTarget(shared.target)
let fileUrl = targetUrl let fileUrl = targetUrl
@@ -39,13 +39,13 @@ extension CliVersionCommand {
let fileString = fileUrl.fileString() let fileString = fileUrl.fileString()
let currentVersion = try gitVersion.currentVersion(in: gitDirectory) let currentVersion = try await gitVersion.currentVersion(in: gitDirectory)
let fileContents = optionalTemplate let fileContents = optionalTemplate
.replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"")
if !shared.dryRun { if !shared.dryRun {
try fileClient.write(string: fileContents, to: fileUrl) try await fileClient.write(string: fileContents, to: fileUrl)
logger.info("Updated version file: \(fileString)") logger.info("Updated version file: \(fileString)")
} else { } else {
logger.info("Would update file contents to:") logger.info("Would update file contents to:")

View File

@@ -8,7 +8,7 @@ import TestSupport
struct CliClientTests { struct CliClientTests {
@Test( @Test(
arguments: TestTarget.testCases arguments: TestArguments.testCases
) )
func testBuild(target: String) async throws { func testBuild(target: String) async throws {
try await run { try await run {
@@ -27,11 +27,12 @@ struct CliClientTests {
} }
@Test( @Test(
arguments: CliClient.BumpOption.allCases arguments: TestArguments.bumpCases
) )
func bump(type: CliClient.BumpOption) async throws { func bump(type: CliClient.BumpOption, optional: Bool) async throws {
let template = optional ? Template.optional("1.0.0") : Template.build("1.0.0")
try await run { try await run {
$0.fileClient.read = { _ in Template.optional("1.0.0") } $0.fileClient.read = { _ in template }
} operation: { } operation: {
let client = CliClient.liveValue let client = CliClient.liveValue
let output = try await client.bump( let output = try await client.bump(
@@ -50,7 +51,7 @@ struct CliClientTests {
} }
@Test( @Test(
arguments: TestTarget.testCases arguments: TestArguments.testCases
) )
func generate(target: String) async throws { func generate(target: String) async throws {
// let (stream, continuation) = AsyncStream<Data>.makeStream() // let (stream, continuation) = AsyncStream<Data>.makeStream()
@@ -68,7 +69,7 @@ struct CliClientTests {
} }
@Test( @Test(
arguments: TestTarget.testCases arguments: TestArguments.testCases
) )
func update(target: String) async throws { func update(target: String) async throws {
// let (stream, continuation) = AsyncStream<Data>.makeStream() // let (stream, continuation) = AsyncStream<Data>.makeStream()
@@ -101,6 +102,10 @@ struct CliClientTests {
} }
} }
enum TestTarget { enum TestArguments {
static let testCases = ["bar", "Sources/bar", "/Sources/bar", "./Sources/bar"] static let testCases = ["bar", "Sources/bar", "/Sources/bar", "./Sources/bar"]
static let bumpCases = CliClient.BumpOption.allCases.reduce(into: [(CliClient.BumpOption, Bool)]()) {
$0.append(($1, true))
$0.append(($1, false))
}
} }

View File

@@ -10,7 +10,7 @@ final class GitVersionTests: XCTestCase {
withDependencies({ withDependencies({
$0.logger.logLevel = .debug $0.logger.logLevel = .debug
$0.logger = .liveValue $0.logger = .liveValue
$0.shellClient = .liveValue $0.asyncShellClient = .liveValue
$0.gitVersionClient = .liveValue $0.gitVersionClient = .liveValue
$0.fileClient = .liveValue $0.fileClient = .liveValue
}, operation: { }, operation: {
@@ -26,67 +26,56 @@ final class GitVersionTests: XCTestCase {
.cleanFilePath .cleanFilePath
} }
func test_overrides_work() throws { func test_live() async throws {
try withDependencies {
$0.gitVersionClient.override(with: "blob")
} operation: {
@Dependency(\.gitVersionClient) var versionClient
let version = try versionClient.currentVersion()
XCTAssertEqual(version, "blob")
}
}
func test_live() throws {
@Dependency(\.gitVersionClient) var versionClient: GitVersionClient @Dependency(\.gitVersionClient) var versionClient: GitVersionClient
let version = try versionClient.currentVersion(in: gitDir) let version = try await versionClient.currentVersion(in: gitDir)
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 {
@Dependency(\.shellClient) var shellClient: ShellClient // @Dependency(\.asyncShellClient) var shellClient: ShellClient
//
// XCTAssertNoThrow(
// try shellClient.background(
// .gitCurrentBranch(gitDirectory: gitDir),
// trimmingCharactersIn: .whitespacesAndNewlines
// )
// )
//
// XCTAssertNoThrow(
// try shellClient.background(
// .gitCurrentSha(gitDirectory: gitDir),
// trimmingCharactersIn: .whitespacesAndNewlines
// )
// )
// }
XCTAssertNoThrow( func test_file_client() async throws {
try shellClient.background( try await withTemporaryDirectory { tmpDir in
.gitCurrentBranch(gitDirectory: gitDir),
trimmingCharactersIn: .whitespacesAndNewlines
)
)
XCTAssertNoThrow(
try shellClient.background(
.gitCurrentSha(gitDirectory: gitDir),
trimmingCharactersIn: .whitespacesAndNewlines
)
)
}
func test_file_client() throws {
try withTemporaryDirectory { tmpDir in
@Dependency(\.fileClient) var fileClient @Dependency(\.fileClient) var fileClient
let filePath = tmpDir.appendingPathComponent("blob.txt") let filePath = tmpDir.appendingPathComponent("blob.txt")
try fileClient.write(string: "Blob", to: filePath) try await fileClient.write(string: "Blob", to: filePath)
let contents = try fileClient.read(filePath) let contents = try await fileClient.read(filePath)
.trimmingCharacters(in: .whitespacesAndNewlines) .trimmingCharacters(in: .whitespacesAndNewlines)
XCTAssertEqual(contents, "Blob") XCTAssertEqual(contents, "Blob")
} }
} }
func test_file_client_with_string_path() throws { func test_file_client_with_string_path() async throws {
try withTemporaryDirectory { tmpDir in try await withTemporaryDirectory { tmpDir in
@Dependency(\.fileClient) var fileClient @Dependency(\.fileClient) var fileClient
let filePath = tmpDir.appendingPathComponent("blob.txt") let filePath = tmpDir.appendingPathComponent("blob.txt")
let fileString = filePath.cleanFilePath let fileString = filePath.cleanFilePath
try fileClient.write(string: "Blob", to: fileString) try await fileClient.write(string: "Blob", to: fileString)
let contents = try fileClient.read(fileString) let contents = try await fileClient.read(fileString)
.trimmingCharacters(in: .whitespacesAndNewlines) .trimmingCharacters(in: .whitespacesAndNewlines)
XCTAssertEqual(contents, "Blob") XCTAssertEqual(contents, "Blob")