This commit is contained in:
2023-03-14 17:03:24 -04:00
parent 78bfa7863a
commit 8dc72e4a95
11 changed files with 185 additions and 139 deletions

View File

@@ -9,7 +9,8 @@ let package = Package(
], ],
products: [ products: [
.library(name: "GitVersion", targets: ["GitVersion"]), .library(name: "GitVersion", targets: ["GitVersion"]),
.plugin(name: "GenerateVersionPlugin", targets: ["GenerateVersionPlugin"]) .plugin(name: "GenerateVersionPlugin", targets: ["GenerateVersionPlugin"]),
.plugin(name: "UpdateVersionPlugin", targets: ["UpdateVersionPlugin"])
], ],
dependencies: [ dependencies: [
.package(url: "https://github.com/m-housh/swift-shell-client.git", from: "0.1.0"), .package(url: "https://github.com/m-housh/swift-shell-client.git", from: "0.1.0"),
@@ -48,6 +49,21 @@ let package = Package(
dependencies: [ dependencies: [
"git-version" "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"
]
) )
] ]
) )

View File

@@ -7,7 +7,7 @@ struct GenerateVersionPlugin: CommandPlugin {
func performCommand(context: PluginContext, arguments: [String]) async throws { func performCommand(context: PluginContext, arguments: [String]) async throws {
let gitVersion = try context.tool(named: "git-version") let gitVersion = try context.tool(named: "git-version")
var arguments = ["generate"] + arguments let arguments = ["generate"] + arguments
for target in context.package.targets { for target in context.package.targets {
guard let target = target as? SourceModuleTarget, guard let target = target as? SourceModuleTarget,

View File

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

View File

@@ -116,7 +116,7 @@ fileprivate struct GitVersion {
.init( .init(
shell: .env, shell: .env,
environment: nil, environment: nil,
in: workingDirectory, in: workingDirectory ?? FileManager.default.currentDirectoryPath,
argument.arguments argument.arguments
) )
} }

View File

@@ -100,90 +100,92 @@ public struct SwiftBuild {
} }
} }
extension ShellClient { //extension ShellClient {
//
/// Reads contents at the given file path, then allows the caller to act on the // /// 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. // /// results after "nil" has been replaced with the current version string.
/// // ///
/// - Parameters: // /// - Parameters:
/// - filePath: The file path to replace nil in. // /// - filePath: The file path to replace nil in.
/// - workingDirectory: Customize the working directory for the command. // /// - workingDirectory: Customize the working directory for the command.
/// - closure: The closure to run with the updated file content string. // /// - closure: The closure to run with the updated file content string.
public func replacingNilWithVersionString( // public func replacingNilWithVersionString(
in filePath: String, // in filePath: String,
from workingDirectory: String? = nil, // from workingDirectory: String? = nil,
_ closure: @escaping (String) throws -> Void // _ closure: @escaping (String) throws -> Void
) throws { // ) throws {
@Dependency(\.fileClient) var fileClient: FileClient // @Dependency(\.fileClient) var fileClient: FileClient
@Dependency(\.gitVersionClient) var gitClient: GitVersionClient // @Dependency(\.gitVersionClient) var gitClient: GitVersionClient
@Dependency(\.logger) var logger: Logger // @Dependency(\.logger) var logger: Logger
//
let currentVersion = try gitClient.currentVersion(in: workingDirectory) // let currentVersion = try gitClient.currentVersion(in: workingDirectory)
let originalContents = try fileClient.readAsString(path: filePath) // let originalContents = try fileClient.readAsString(path: filePath)
//
logger.debug("Setting version: \(currentVersion)") // logger.debug("Setting version: \(currentVersion)")
//
let updatedContents = originalContents // let updatedContents = originalContents
.replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"") // .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"")
//
logger.debug("Set version") // logger.debug("Set version")
// logger.debug("Updated contents:")
try closure(updatedContents) // 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. // /// Replace nil in the given file path and then run the given closure.
/// // ///
/// - Parameters: // /// > Note: The file contents will be reset back to nil after the closure operation.
/// - filePath: The file path to replace nil in. // ///
/// - workingDirectory: Customize the working directory for the command. // /// - Parameters:
/// - build: The swift builder to use. // /// - filePath: The file path to replace nil in.
public func replacingNilWithVersionString( // /// - workingDirectory: Customize the working directory for the command.
in filePath: String, // /// - build: The swift builder to use.
from workingDirectory: String? = nil, // public func replacingNilWithVersionString(
_ closure: @escaping () throws -> Void // in filePath: String,
) throws { // from workingDirectory: String? = nil,
@Dependency(\.fileClient) var fileClient: FileClient // _ closure: @escaping () throws -> Void
@Dependency(\.logger) var logger: Logger // ) throws {
// @Dependency(\.fileClient) var fileClient: FileClient
// grab the original contents, to set it back when done. // @Dependency(\.logger) var logger: Logger
let originalContents = try fileClient.readAsString(path: filePath) //
// // grab the original contents, to set it back when done.
try self.replacingNilWithVersionString( // let originalContents = try fileClient.readAsString(path: filePath)
in: filePath, //
from: workingDirectory // try self.replacingNilWithVersionString(
) { update in // in: filePath,
try fileClient.write(string: update, to: filePath) // from: workingDirectory
defer { try! fileClient.write(string: originalContents, to: filePath) } // ) { update in
// try fileClient.write(string: update, to: filePath)
try closure() // 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. //
/// // /// Replace nil in the given file path and then build the project, using the
/// > Note: The file contents will be reset back to nil after the build operation. // /// given builder.
/// // ///
/// - Parameters: // /// > Note: The file contents will be reset back to nil after the build operation.
/// - filePath: The file path to replace nil in. // ///
/// - workingDirectory: Customize the working directory for the command. // /// - Parameters:
/// - build: The swift builder to use. // /// - filePath: The file path to replace nil in.
public func replacingNilWithVersionString( // /// - workingDirectory: Customize the working directory for the command.
in filePath: String, // /// - build: The swift builder to use.
from workingDirectory: String? = nil, // public func replacingNilWithVersionString(
build: SwiftBuild // in filePath: String,
) throws { // from workingDirectory: String? = nil,
try replacingNilWithVersionString( // build: SwiftBuild
in: filePath, // ) throws {
from: workingDirectory, // try replacingNilWithVersionString(
build.run // in: filePath,
) // from: workingDirectory,
} // build.run
} // )
// }
//}
fileprivate extension Array where Element == SwiftBuild.Argument { fileprivate extension Array where Element == SwiftBuild.Argument {

View File

@@ -42,8 +42,3 @@ fileprivate enum GenerationError: Error {
case fileExists(path: String) case fileExists(path: String)
} }
fileprivate let template = """
// Do not set this variable, it is set during the build process.
let VERSION: String? = nil
"""

View File

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

View File

@@ -1,3 +1,4 @@
import ArgumentParser
import Foundation import Foundation
func parseTarget(_ target: String) -> URL { func parseTarget(_ target: String) -> URL {
@@ -18,3 +19,27 @@ extension URL {
.replacingOccurrences(of: "file://", with: "") .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
}

View File

@@ -19,43 +19,35 @@ extension GitVersionCommand {
var gitDirectory: String? = nil var gitDirectory: String? = nil
func run() throws { func run() throws {
@Dependency(\.logger) var logger: Logger try withDependencies {
@Dependency(\.fileClient) var fileClient: FileClient $0.logger.logLevel = shared.verbose ? .debug : .info
$0.fileClient = .liveValue
$0.gitVersionClient = .liveValue
$0.shellClient = .liveValue
} operation: {
@Dependency(\.gitVersionClient) var gitVersion @Dependency(\.gitVersionClient) var gitVersion
@Dependency(\.fileClient) var fileClient
@Dependency(\.logger) var logger
@Dependency(\.shellClient) var shell @Dependency(\.shellClient) var shell
let targetUrl = parseTarget(shared.target) let targetUrl = parseTarget(shared.target)
let fileUrl = targetUrl.appendingPathComponent(shared.fileName) let fileUrl = targetUrl
.appendingPathComponent(shared.fileName)
let fileString = fileUrl.fileString() let fileString = fileUrl.fileString()
// guard FileManager.default.fileExists(atPath: fileUrl.absoluteString) else { let currentVersion = try gitVersion.currentVersion(in: gitDirectory)
// logger.info("Version file does not exist.")
// throw UpdateError.versionFileDoesNotExist(path: fileString)
// }
let currentVersion = try gitVersion.currentVersion() let fileContents = template
let cwd = FileManager.default.currentDirectoryPath .replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"")
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 { if !shared.dryRun {
try fileClient.write(string: updatedContents, to: fileUrl) try 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:")
logger.info("\(updatedContents)") logger.info("\(fileContents)")
}
} }
} }
} }

View File

@@ -1,2 +1,2 @@
// Do not set this variable, it is set during the build process. // Do not set this variable, it is set during the build process.
let VERSION: String? = "" let VERSION: String? = nil

View File

@@ -35,6 +35,7 @@ final class GitVersionTests: XCTestCase {
// 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")
// print(FileManager.default.currentDirectoryPath)
// let other = try versionClient.currentVersion() // let other = try versionClient.currentVersion()
// XCTAssertEqual(version, other) // XCTAssertEqual(version, other)