diff --git a/Package.swift b/Package.swift index 254c306..7bfbef3 100644 --- a/Package.swift +++ b/Package.swift @@ -9,7 +9,8 @@ let package = Package( ], products: [ .library(name: "GitVersion", targets: ["GitVersion"]), - .plugin(name: "GenerateVersionPlugin", targets: ["GenerateVersionPlugin"]) + .plugin(name: "GenerateVersionPlugin", targets: ["GenerateVersionPlugin"]), + .plugin(name: "UpdateVersionPlugin", targets: ["UpdateVersionPlugin"]) ], dependencies: [ .package(url: "https://github.com/m-housh/swift-shell-client.git", from: "0.1.0"), @@ -48,6 +49,21 @@ let package = Package( dependencies: [ "git-version" ] + ), + .plugin( + name: "UpdateVersionPlugin", + capability: .command( + intent: .custom( + verb: "update-version", + description: "Updates a version file in the given target." + ), + permissions: [ + .writeToPackageDirectory(reason: "Update a version file in the target's directory.") + ] + ), + dependencies: [ + "git-version" + ] ) ] ) diff --git a/Plugins/GenerateVersionPlugin/GenerateVersionPlugin.swift b/Plugins/GenerateVersionPlugin/GenerateVersionPlugin.swift index 025a344..5db858e 100644 --- a/Plugins/GenerateVersionPlugin/GenerateVersionPlugin.swift +++ b/Plugins/GenerateVersionPlugin/GenerateVersionPlugin.swift @@ -7,7 +7,7 @@ struct GenerateVersionPlugin: CommandPlugin { func performCommand(context: PluginContext, arguments: [String]) async throws { let gitVersion = try context.tool(named: "git-version") - var arguments = ["generate"] + arguments + let arguments = ["generate"] + arguments for target in context.package.targets { guard let target = target as? SourceModuleTarget, diff --git a/Plugins/UpdateVersionPlugin/UpdateVersionPlugin.swift b/Plugins/UpdateVersionPlugin/UpdateVersionPlugin.swift new file mode 100644 index 0000000..559b270 --- /dev/null +++ b/Plugins/UpdateVersionPlugin/UpdateVersionPlugin.swift @@ -0,0 +1,29 @@ +import PackagePlugin +import Foundation + +@main +struct UpdateVersionPlugin: CommandPlugin { + + func performCommand(context: PluginContext, arguments: [String]) async throws { + let gitVersion = try context.tool(named: "git-version") + + let arguments = ["update"] + arguments + + for target in context.package.targets { + guard let target = target as? SourceModuleTarget, + arguments.first(where: { $0.contains(target.name) }) != nil + else { continue } + + let process = Process() + process.executableURL = URL(fileURLWithPath: gitVersion.path.string) + process.arguments = arguments + try process.run() + process.waitUntilExit() + + guard process.terminationReason == .exit && process.terminationStatus == 0 else { + Diagnostics.error("Reason: \(process.terminationReason), status: \(process.terminationStatus)") + return + } + } + } +} diff --git a/Sources/GitVersion/GitVersionClient.swift b/Sources/GitVersion/GitVersionClient.swift index f278699..f91bfcf 100644 --- a/Sources/GitVersion/GitVersionClient.swift +++ b/Sources/GitVersion/GitVersionClient.swift @@ -116,7 +116,7 @@ fileprivate struct GitVersion { .init( shell: .env, environment: nil, - in: workingDirectory, + in: workingDirectory ?? FileManager.default.currentDirectoryPath, argument.arguments ) } diff --git a/Sources/GitVersion/SwiftBuild.swift b/Sources/GitVersion/SwiftBuild.swift index da5a095..cf01c65 100644 --- a/Sources/GitVersion/SwiftBuild.swift +++ b/Sources/GitVersion/SwiftBuild.swift @@ -100,90 +100,92 @@ public struct SwiftBuild { } } -extension ShellClient { - - /// Reads contents at the given file path, then allows the caller to act on the - /// results after "nil" has been replaced with the current version string. - /// - /// - Parameters: - /// - filePath: The file path to replace nil in. - /// - workingDirectory: Customize the working directory for the command. - /// - closure: The closure to run with the updated file content string. - public func replacingNilWithVersionString( - in filePath: String, - from workingDirectory: String? = nil, - _ closure: @escaping (String) throws -> Void - ) throws { - @Dependency(\.fileClient) var fileClient: FileClient - @Dependency(\.gitVersionClient) var gitClient: GitVersionClient - @Dependency(\.logger) var logger: Logger - - let currentVersion = try gitClient.currentVersion(in: workingDirectory) - let originalContents = try fileClient.readAsString(path: filePath) - - logger.debug("Setting version: \(currentVersion)") - - let updatedContents = originalContents - .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") - - logger.debug("Set version") - - try closure(updatedContents) - } - - - /// Replace nil in the given file path and then run the given closure. - /// - /// > Note: The file contents will be reset back to nil after the closure operation. - /// - /// - Parameters: - /// - filePath: The file path to replace nil in. - /// - workingDirectory: Customize the working directory for the command. - /// - build: The swift builder to use. - public func replacingNilWithVersionString( - in filePath: String, - from workingDirectory: String? = nil, - _ closure: @escaping () throws -> Void - ) throws { - @Dependency(\.fileClient) var fileClient: FileClient - @Dependency(\.logger) var logger: Logger - - // grab the original contents, to set it back when done. - let originalContents = try fileClient.readAsString(path: filePath) - - try self.replacingNilWithVersionString( - in: filePath, - from: workingDirectory - ) { update in - try fileClient.write(string: update, to: filePath) - defer { try! fileClient.write(string: originalContents, to: filePath) } - - try closure() - } - - } - - /// Replace nil in the given file path and then build the project, using the - /// given builder. - /// - /// > Note: The file contents will be reset back to nil after the build operation. - /// - /// - Parameters: - /// - filePath: The file path to replace nil in. - /// - workingDirectory: Customize the working directory for the command. - /// - build: The swift builder to use. - public func replacingNilWithVersionString( - in filePath: String, - from workingDirectory: String? = nil, - build: SwiftBuild - ) throws { - try replacingNilWithVersionString( - in: filePath, - from: workingDirectory, - build.run - ) - } -} +//extension ShellClient { +// +// /// Reads contents at the given file path, then allows the caller to act on the +// /// results after "nil" has been replaced with the current version string. +// /// +// /// - Parameters: +// /// - filePath: The file path to replace nil in. +// /// - workingDirectory: Customize the working directory for the command. +// /// - closure: The closure to run with the updated file content string. +// public func replacingNilWithVersionString( +// in filePath: String, +// from workingDirectory: String? = nil, +// _ closure: @escaping (String) throws -> Void +// ) throws { +// @Dependency(\.fileClient) var fileClient: FileClient +// @Dependency(\.gitVersionClient) var gitClient: GitVersionClient +// @Dependency(\.logger) var logger: Logger +// +// let currentVersion = try gitClient.currentVersion(in: workingDirectory) +// let originalContents = try fileClient.readAsString(path: filePath) +// +// logger.debug("Setting version: \(currentVersion)") +// +// let updatedContents = originalContents +// .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") +// +// logger.debug("Set version") +// logger.debug("Updated contents:") +// logger.debug("\(updatedContents)") +// +// try closure(updatedContents) +// } +// +// +// /// Replace nil in the given file path and then run the given closure. +// /// +// /// > Note: The file contents will be reset back to nil after the closure operation. +// /// +// /// - Parameters: +// /// - filePath: The file path to replace nil in. +// /// - workingDirectory: Customize the working directory for the command. +// /// - build: The swift builder to use. +// public func replacingNilWithVersionString( +// in filePath: String, +// from workingDirectory: String? = nil, +// _ closure: @escaping () throws -> Void +// ) throws { +// @Dependency(\.fileClient) var fileClient: FileClient +// @Dependency(\.logger) var logger: Logger +// +// // grab the original contents, to set it back when done. +// let originalContents = try fileClient.readAsString(path: filePath) +// +// try self.replacingNilWithVersionString( +// in: filePath, +// from: workingDirectory +// ) { update in +// try fileClient.write(string: update, to: filePath) +// defer { try! fileClient.write(string: originalContents, to: filePath) } +// +// try closure() +// } +// +// } +// +// /// Replace nil in the given file path and then build the project, using the +// /// given builder. +// /// +// /// > Note: The file contents will be reset back to nil after the build operation. +// /// +// /// - Parameters: +// /// - filePath: The file path to replace nil in. +// /// - workingDirectory: Customize the working directory for the command. +// /// - build: The swift builder to use. +// public func replacingNilWithVersionString( +// in filePath: String, +// from workingDirectory: String? = nil, +// build: SwiftBuild +// ) throws { +// try replacingNilWithVersionString( +// in: filePath, +// from: workingDirectory, +// build.run +// ) +// } +//} fileprivate extension Array where Element == SwiftBuild.Argument { diff --git a/Sources/git-version/GenerateCommand.swift b/Sources/git-version/GenerateCommand.swift index c914715..3873d2e 100644 --- a/Sources/git-version/GenerateCommand.swift +++ b/Sources/git-version/GenerateCommand.swift @@ -42,8 +42,3 @@ fileprivate enum GenerationError: Error { case fileExists(path: String) } -fileprivate let template = """ -// Do not set this variable, it is set during the build process. -let VERSION: String? = nil - -""" diff --git a/Sources/git-version/GitVersionCommand.swift b/Sources/git-version/GitVersionCommand.swift index cd84d38..9b8971d 100644 --- a/Sources/git-version/GitVersionCommand.swift +++ b/Sources/git-version/GitVersionCommand.swift @@ -14,17 +14,3 @@ struct GitVersionCommand: ParsableCommand { ) } -struct SharedOptions: ParsableArguments { - - @Argument(help: "The target for the version file.") - var target: String - - @Option( - name: .customLong("filename"), - help: "Specify the file name for the version file." - ) - var fileName: String = "Version.swift" - - @Flag(name: .customLong("dry-run")) - var dryRun: Bool = false -} diff --git a/Sources/git-version/Helpers.swift b/Sources/git-version/Helpers.swift index 2e6e269..2fa742c 100644 --- a/Sources/git-version/Helpers.swift +++ b/Sources/git-version/Helpers.swift @@ -1,3 +1,4 @@ +import ArgumentParser import Foundation func parseTarget(_ target: String) -> URL { @@ -18,3 +19,27 @@ extension URL { .replacingOccurrences(of: "file://", with: "") } } + +let template = """ +// Do not set this variable, it is set during the build process. +let VERSION: String? = nil + +""" + +struct SharedOptions: ParsableArguments { + + @Argument(help: "The target for the version file.") + var target: String + + @Option( + name: .customLong("filename"), + help: "Specify the file name for the version file." + ) + var fileName: String = "Version.swift" + + @Flag(name: .customLong("dry-run")) + var dryRun: Bool = false + + @Flag(name: .long, help: "Increase logging level.") + var verbose: Bool = false +} diff --git a/Sources/git-version/UpdateCommand.swift b/Sources/git-version/UpdateCommand.swift index 979f3ac..296f23d 100644 --- a/Sources/git-version/UpdateCommand.swift +++ b/Sources/git-version/UpdateCommand.swift @@ -19,43 +19,35 @@ extension GitVersionCommand { var gitDirectory: String? = nil func run() throws { - @Dependency(\.logger) var logger: Logger - @Dependency(\.fileClient) var fileClient: FileClient - @Dependency(\.gitVersionClient) var gitVersion - @Dependency(\.shellClient) var shell + try withDependencies { + $0.logger.logLevel = shared.verbose ? .debug : .info + $0.fileClient = .liveValue + $0.gitVersionClient = .liveValue + $0.shellClient = .liveValue + } operation: { + @Dependency(\.gitVersionClient) var gitVersion + @Dependency(\.fileClient) var fileClient + @Dependency(\.logger) var logger + @Dependency(\.shellClient) var shell - let targetUrl = parseTarget(shared.target) - let fileUrl = targetUrl.appendingPathComponent(shared.fileName) - let fileString = fileUrl.fileString() + let targetUrl = parseTarget(shared.target) + let fileUrl = targetUrl + .appendingPathComponent(shared.fileName) -// guard FileManager.default.fileExists(atPath: fileUrl.absoluteString) else { -// logger.info("Version file does not exist.") -// throw UpdateError.versionFileDoesNotExist(path: fileString) -// } + let fileString = fileUrl.fileString() - let currentVersion = try gitVersion.currentVersion() - let cwd = FileManager.default.currentDirectoryPath - logger.info("CWD: \(cwd)") - logger.info("Git version: \(currentVersion)") + let currentVersion = try gitVersion.currentVersion(in: gitDirectory) - var updatedContents: String = "" - try withDependencies({ - $0.logger.logLevel = .debug - }, operation: { - try shell.replacingNilWithVersionString( - in: fileString -// from: gitDirectory - ) { - updatedContents = $0 + let fileContents = template + .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") + + if !shared.dryRun { + try fileClient.write(string: fileContents, to: fileUrl) + logger.info("Updated version file: \(fileString)") + } else { + logger.info("Would update file contents to:") + logger.info("\(fileContents)") } - }) - - if !shared.dryRun { - try fileClient.write(string: updatedContents, to: fileUrl) - logger.info("Updated version file: \(fileString)") - } else { - logger.info("Would update file contents to:") - logger.info("\(updatedContents)") } } } diff --git a/Sources/git-version/Version.swift b/Sources/git-version/Version.swift index 237c82b..c5554ab 100644 --- a/Sources/git-version/Version.swift +++ b/Sources/git-version/Version.swift @@ -1,2 +1,2 @@ // Do not set this variable, it is set during the build process. -let VERSION: String? = "" +let VERSION: String? = nil diff --git a/Tests/GitVersionTests/GitVersionTests.swift b/Tests/GitVersionTests/GitVersionTests.swift index 4ba6bde..af2800c 100644 --- a/Tests/GitVersionTests/GitVersionTests.swift +++ b/Tests/GitVersionTests/GitVersionTests.swift @@ -35,6 +35,7 @@ final class GitVersionTests: XCTestCase { // can't really have a predictable result for the live client. XCTAssertNotEqual(version, "blob") +// print(FileManager.default.currentDirectoryPath) // let other = try versionClient.currentVersion() // XCTAssertEqual(version, other)