From 78bfa7863a9b0609e45a62392ee56e154eaf4d52 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Tue, 14 Mar 2023 16:23:16 -0400 Subject: [PATCH] wip --- .../xcschemes/swift-git-version.xcscheme | 14 ++++ Makefile | 5 -- Package.swift | 49 ++++---------- .../GenerateVersionPlugin.swift | 30 +++++++++ .../GitVersionBuildPlugin.swift | 51 -------------- .../GitVersionPlugin/GitVersionPlugin.swift | 34 ---------- Sources/GitVersion/FileClient.swift | 10 +++ Sources/GitVersion/SwiftBuild.swift | 2 - Sources/build-example/Build.swift | 29 -------- Sources/example/Version.swift | 2 - Sources/example/example.swift | 23 ------- .../GitVersionBuilder.swift | 34 ---------- Sources/git-version/GenerateCommand.swift | 49 ++++++++++++++ Sources/git-version/GitVersionCommand.swift | 30 +++++++++ Sources/git-version/Helpers.swift | 20 ++++++ Sources/git-version/UpdateCommand.swift | 66 +++++++++++++++++++ Sources/git-version/Version.swift | 2 + Tests/GitVersionTests/GitVersionTests.swift | 4 +- 18 files changed, 237 insertions(+), 217 deletions(-) create mode 100644 Plugins/GenerateVersionPlugin/GenerateVersionPlugin.swift delete mode 100644 Plugins/GitVersionBuildPlugin/GitVersionBuildPlugin.swift delete mode 100644 Plugins/GitVersionPlugin/GitVersionPlugin.swift delete mode 100644 Sources/build-example/Build.swift delete mode 100644 Sources/example/Version.swift delete mode 100644 Sources/example/example.swift delete mode 100644 Sources/git-version-builder/GitVersionBuilder.swift create mode 100644 Sources/git-version/GenerateCommand.swift create mode 100644 Sources/git-version/GitVersionCommand.swift create mode 100644 Sources/git-version/Helpers.swift create mode 100644 Sources/git-version/UpdateCommand.swift create mode 100644 Sources/git-version/Version.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/swift-git-version.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/swift-git-version.xcscheme index 8d3c550..8afbdc3 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/swift-git-version.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/swift-git-version.xcscheme @@ -20,6 +20,20 @@ ReferencedContainer = "container:"> + + + + [PackagePlugin.Command] { - - guard let target = target as? SourceModuleTarget else { return [] } - let buildTool = try context.tool(named: "git-version-builder") - - let outputDir = context.pluginWorkDirectory - .appending(subpath: target.name) - - try FileManager.default - .createDirectory(atPath: outputDir.string, withIntermediateDirectories: true) - - let inputFiles = target.sourceFiles - .filter({ $0.type == .source && $0.path.stem == "Version" }) - .map(\.path) - - guard inputFiles.count == 1 else { return [] } - - let outputFile = outputDir.appending(subpath: "Version.generated.swift") - - print("Input swift files: \(inputFiles)") - -// let originalContents = try String(contentsOfFile: inputFiles.first!.string) -// let updatedContents = originalContents.replacingOccurrences(of: "nil", with: "\"0.1.123\"") -// -// print("Updated contents") -// print(updatedContents) - - // this fails. - return [ - .buildCommand( - displayName: "Git Version Build Plugin", - executable: buildTool.path, - arguments: [ - inputFiles.first!, - outputFile.string - ] -// , -// outputFiles: [outputFile] - ) - ] - } -} diff --git a/Plugins/GitVersionPlugin/GitVersionPlugin.swift b/Plugins/GitVersionPlugin/GitVersionPlugin.swift deleted file mode 100644 index daf01b9..0000000 --- a/Plugins/GitVersionPlugin/GitVersionPlugin.swift +++ /dev/null @@ -1,34 +0,0 @@ -import PackagePlugin -import Foundation - -@main -struct GitVersionPlugin: CommandPlugin { - - func performCommand(context: PackagePlugin.PluginContext, arguments: [String]) async throws { - let tool = try context.tool(named: "build-example") - let url = URL(fileURLWithPath: tool.path.string) - - for target in context.package.targets { - guard let target = target as? SourceModuleTarget else { continue } - let process = Process() - process.executableURL = url - try process.run() - process.waitUntilExit() - - if process.terminationReason == .exit && process.terminationStatus == 0 { - print("Done building in: \(target.directory)") - } else { - let problem = "\(process.terminationReason): \(process.terminationStatus)" - Diagnostics.error("\(problem)") - } - } - } - - -// func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { -// guard let target = target.sourceModule else { return [] } -// let tool = try context.tool(named: "build-example") -// -// -// } -} diff --git a/Sources/GitVersion/FileClient.swift b/Sources/GitVersion/FileClient.swift index d736d89..684de2d 100644 --- a/Sources/GitVersion/FileClient.swift +++ b/Sources/GitVersion/FileClient.swift @@ -106,10 +106,20 @@ public struct FileClient { try self.write(data, url) } + /// Write's the given string to a file. + /// + /// - Parameters: + /// - 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) } + /// Write's the the string to a file path. + /// + /// - Parameters: + /// - 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) diff --git a/Sources/GitVersion/SwiftBuild.swift b/Sources/GitVersion/SwiftBuild.swift index 447d1de..da5a095 100644 --- a/Sources/GitVersion/SwiftBuild.swift +++ b/Sources/GitVersion/SwiftBuild.swift @@ -105,8 +105,6 @@ 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. /// - /// > 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. diff --git a/Sources/build-example/Build.swift b/Sources/build-example/Build.swift deleted file mode 100644 index b9084e5..0000000 --- a/Sources/build-example/Build.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation -import GitVersion -import ShellClient - -/// Shows the intended use-case for building a command line tool that set's the version -/// based on the tag in the git worktree. - -@main -public struct Build { - public static func main() throws { - @Dependency(\.shellClient) var shell: ShellClient - - let gitDir = URL(fileURLWithPath: #file) - .deletingLastPathComponent() - .deletingLastPathComponent() - .deletingLastPathComponent() - - try withDependencies { - $0.gitVersionClient = .liveValue - $0.logger.logLevel = .debug - } operation: { - try shell.replacingNilWithVersionString( - in: "Sources/example/Version.swift", - from: gitDir.absoluteString, - build: SwiftBuild.release() - ) - } - } -} diff --git a/Sources/example/Version.swift b/Sources/example/Version.swift deleted file mode 100644 index 2a8f24d..0000000 --- a/Sources/example/Version.swift +++ /dev/null @@ -1,2 +0,0 @@ -// Do not change this value, it is set by the build script. -let VERSION: String? = nil diff --git a/Sources/example/example.swift b/Sources/example/example.swift deleted file mode 100644 index f265341..0000000 --- a/Sources/example/example.swift +++ /dev/null @@ -1,23 +0,0 @@ -import ArgumentParser -import ShellClient - -/// An example of using the git version client with a command line tool -/// The ``VERSION`` variable get's set during the build process. -@main -public struct Example: ParsableCommand { - - public static let configuration: CommandConfiguration = .init( - abstract: "An example of using the `GitVersion` command to set the version for a command line app.", - version: VERSION ?? "0.0.0" - ) - - public init() { } - - public func run() throws { - @Dependency(\.logger) var logger: Logger - - let version = (VERSION ?? "0.0.0").blue - logger.info("Version: \(version)") - } - -} diff --git a/Sources/git-version-builder/GitVersionBuilder.swift b/Sources/git-version-builder/GitVersionBuilder.swift deleted file mode 100644 index fa1fb25..0000000 --- a/Sources/git-version-builder/GitVersionBuilder.swift +++ /dev/null @@ -1,34 +0,0 @@ -import ArgumentParser -import Dependencies -import GitVersion -import ShellClient - -@main -public struct GitVersionBuilder: AsyncParsableCommand { - - public init() { } - - @Argument - var input: String - - @Argument - var output: String - - public func run() async throws { - @Dependency(\.logger) var logger - @Dependency(\.fileClient) var fileClient - @Dependency(\.shellClient) var shell: ShellClient - - logger.debug("Building with input file: \(input)") - logger.debug("Output file: \(output)") - - try shell.replacingNilWithVersionString( - in: input - ) { update in - logger.debug("Updating with:\n\(update)") - try fileClient.write(string: update, to: output) - } - - } -} - diff --git a/Sources/git-version/GenerateCommand.swift b/Sources/git-version/GenerateCommand.swift new file mode 100644 index 0000000..c914715 --- /dev/null +++ b/Sources/git-version/GenerateCommand.swift @@ -0,0 +1,49 @@ +import ArgumentParser +import Dependencies +import Foundation +import GitVersion +import ShellClient + +extension GitVersionCommand { + + 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." + ) + + @OptionGroup var shared: SharedOptions + + func run() throws { + @Dependency(\.logger) var logger: Logger + @Dependency(\.fileClient) var fileClient + + let targetUrl = parseTarget(shared.target) + let fileUrl = targetUrl.appendingPathComponent(shared.fileName) + + let fileString = fileUrl.fileString() + + guard !FileManager.default.fileExists(atPath: fileUrl.absoluteString) else { + logger.info("File already exists at path.") + throw GenerationError.fileExists(path: fileString) + } + + if !shared.dryRun { + try fileClient.write(string: template, to: fileUrl) + logger.info("Generated file at: \(fileString)") + } else { + logger.info("Would generate file at: \(fileString)") + } + + } + } +} + +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 new file mode 100644 index 0000000..cd84d38 --- /dev/null +++ b/Sources/git-version/GitVersionCommand.swift @@ -0,0 +1,30 @@ +import ArgumentParser +import Foundation + +@main +struct GitVersionCommand: ParsableCommand { + + static var configuration: CommandConfiguration = .init( + commandName: "git-version", + version: VERSION ?? "0.0.0", + subcommands: [ + Generate.self, + Update.self + ] + ) +} + +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 new file mode 100644 index 0000000..2e6e269 --- /dev/null +++ b/Sources/git-version/Helpers.swift @@ -0,0 +1,20 @@ +import Foundation + +func parseTarget(_ target: String) -> URL { + let url = URL(fileURLWithPath: target) + let urlTest = url + .deletingLastPathComponent() + + guard urlTest.lastPathComponent == "Sources" else { + return URL(fileURLWithPath: "Sources") + .appendingPathComponent(target) + } + return url +} + +extension URL { + func fileString() -> String { + self.absoluteString + .replacingOccurrences(of: "file://", with: "") + } +} diff --git a/Sources/git-version/UpdateCommand.swift b/Sources/git-version/UpdateCommand.swift new file mode 100644 index 0000000..979f3ac --- /dev/null +++ b/Sources/git-version/UpdateCommand.swift @@ -0,0 +1,66 @@ +import ArgumentParser +import Foundation +import GitVersion +import ShellClient + +extension GitVersionCommand { + + struct Update: ParsableCommand { + static var configuration: CommandConfiguration = .init( + abstract: "Updates a version string to the git tag or git sha." + ) + + @OptionGroup var shared: SharedOptions + + @Option( + name: .customLong("git-directory"), + help: "The git directory for the version." + ) + 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 + + let targetUrl = parseTarget(shared.target) + let fileUrl = targetUrl.appendingPathComponent(shared.fileName) + let fileString = fileUrl.fileString() + +// guard FileManager.default.fileExists(atPath: fileUrl.absoluteString) else { +// logger.info("Version file does not exist.") +// throw UpdateError.versionFileDoesNotExist(path: fileString) +// } + + let currentVersion = try gitVersion.currentVersion() + let cwd = FileManager.default.currentDirectoryPath + logger.info("CWD: \(cwd)") + logger.info("Git version: \(currentVersion)") + + var updatedContents: String = "" + try withDependencies({ + $0.logger.logLevel = .debug + }, operation: { + try shell.replacingNilWithVersionString( + in: fileString +// from: gitDirectory + ) { + updatedContents = $0 + } + }) + + 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)") + } + } + } +} + +fileprivate enum UpdateError: Error { + case versionFileDoesNotExist(path: String) +} diff --git a/Sources/git-version/Version.swift b/Sources/git-version/Version.swift new file mode 100644 index 0000000..237c82b --- /dev/null +++ b/Sources/git-version/Version.swift @@ -0,0 +1,2 @@ +// Do not set this variable, it is set during the build process. +let VERSION: String? = "" diff --git a/Tests/GitVersionTests/GitVersionTests.swift b/Tests/GitVersionTests/GitVersionTests.swift index 4b456aa..4ba6bde 100644 --- a/Tests/GitVersionTests/GitVersionTests.swift +++ b/Tests/GitVersionTests/GitVersionTests.swift @@ -35,8 +35,8 @@ final class GitVersionTests: XCTestCase { // can't really have a predictable result for the live client. XCTAssertNotEqual(version, "blob") - let other = try versionClient.currentVersion() - XCTAssertEqual(version, other) +// let other = try versionClient.currentVersion() +// XCTAssertEqual(version, other) }) }