wip
This commit is contained in:
@@ -18,6 +18,15 @@
|
||||
"version" : "4.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-argument-parser",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-argument-parser.git",
|
||||
"state" : {
|
||||
"revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a",
|
||||
"version" : "1.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-clocks",
|
||||
"kind" : "remoteSourceControl",
|
||||
@@ -36,6 +45,24 @@
|
||||
"version" : "0.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-docc-plugin",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-docc-plugin.git",
|
||||
"state" : {
|
||||
"revision" : "10bc670db657d11bdd561e07de30a9041311b2b1",
|
||||
"version" : "1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-docc-symbolkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-docc-symbolkit",
|
||||
"state" : {
|
||||
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
|
||||
"version" : "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-log",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
18
Makefile
18
Makefile
@@ -2,10 +2,13 @@ DOCC_TARGET ?= GitVersion
|
||||
DOCC_BASEPATH = $(shell basename "$(PWD)")
|
||||
DOCC_DIR ?= ./docs
|
||||
|
||||
clean:
|
||||
rm -rf .build
|
||||
|
||||
build-and-run:
|
||||
swift run build-example
|
||||
./.build/debug/example --help
|
||||
./.build/debug/example
|
||||
swift run -c release build-example
|
||||
./.build/release/example --help
|
||||
./.build/release/example
|
||||
|
||||
build-documentation:
|
||||
swift package \
|
||||
@@ -22,3 +25,12 @@ preview-documentation:
|
||||
--disable-sandbox \
|
||||
preview-documentation \
|
||||
--target "$(DOCC_TARGET)"
|
||||
|
||||
test-linux:
|
||||
docker run --rm \
|
||||
--volume "$(PWD):$(PWD)" \
|
||||
--workdir "$(PWD)" \
|
||||
swift:5.7-focal \
|
||||
swift test
|
||||
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "swift-git-version",
|
||||
platforms: [
|
||||
.macOS(.v10_15)
|
||||
.macOS(.v12)
|
||||
],
|
||||
products: [
|
||||
.library(name: "GitVersion", targets: ["GitVersion"])
|
||||
.library(name: "GitVersion", targets: ["GitVersion"]),
|
||||
.plugin(name: "GitVersionBuildPlugin", targets: ["GitVersionBuildPlugin"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/m-housh/swift-shell-client.git", from: "0.1.0"),
|
||||
@@ -16,6 +17,13 @@ let package = Package(
|
||||
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2"),
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "git-version-builder",
|
||||
dependencies: [
|
||||
"GitVersion",
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "GitVersion",
|
||||
dependencies: [
|
||||
@@ -34,10 +42,33 @@ let package = Package(
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "ShellClient", package: "swift-shell-client")
|
||||
]
|
||||
,
|
||||
plugins: [
|
||||
.plugin(name: "GitVersionBuildPlugin")
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "GitVersionTests",
|
||||
dependencies: ["GitVersion"]
|
||||
),
|
||||
// .plugin(
|
||||
// name: "GitVersionPlugin",
|
||||
// capability: .command(
|
||||
// intent: .custom(verb: "build-with-version", description: "Build a command line tool with git version."),
|
||||
// permissions: [
|
||||
// .writeToPackageDirectory(reason: "This command builds a command line tool with a git version.")
|
||||
// ]
|
||||
// ),
|
||||
// dependencies: [
|
||||
// "build-example"
|
||||
// ]
|
||||
// ),
|
||||
.plugin(
|
||||
name: "GitVersionBuildPlugin",
|
||||
capability: .buildTool(),
|
||||
dependencies: [
|
||||
"git-version-builder"
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
51
Plugins/GitVersionBuildPlugin/GitVersionBuildPlugin.swift
Normal file
51
Plugins/GitVersionBuildPlugin/GitVersionBuildPlugin.swift
Normal file
@@ -0,0 +1,51 @@
|
||||
import Foundation
|
||||
import PackagePlugin
|
||||
|
||||
@main
|
||||
struct GitVersionBuildPlugin: BuildToolPlugin {
|
||||
|
||||
func createBuildCommands(
|
||||
context: PackagePlugin.PluginContext,
|
||||
target: PackagePlugin.Target
|
||||
) async throws -> [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]
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
34
Plugins/GitVersionPlugin/GitVersionPlugin.swift
Normal file
34
Plugins/GitVersionPlugin/GitVersionPlugin.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
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")
|
||||
//
|
||||
//
|
||||
// }
|
||||
}
|
||||
40
README.md
40
README.md
@@ -1,3 +1,41 @@
|
||||
# swift-git-version
|
||||
|
||||
A description of this package.
|
||||
A swift package that exposes some helpers to set the version of a command line tool to the
|
||||
git tag or the git sha, if a tag is not set for the current commit.
|
||||
|
||||
## Usage
|
||||
|
||||
You can use this in your command line tool via the swift package manager.
|
||||
|
||||
```swift
|
||||
let package = Package(
|
||||
...
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/m-housh/swift-git-version.git", from: "0.1.0")
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "my-executable",
|
||||
dependencies: [
|
||||
...
|
||||
]
|
||||
),
|
||||
.executableTarget(
|
||||
name: "my-executable-builder",
|
||||
dependencies: [
|
||||
.product(name: "GitVersion", package: "swift-git-version")
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
Inside of your executable (`my-executable`) in the above example, you will want to create
|
||||
a file that contains your version variable.
|
||||
|
||||
```swift
|
||||
// Do not set this variable, it is set by the build script.
|
||||
|
||||
let VERSION: String? = nil
|
||||
```
|
||||
|
||||
|
||||
@@ -164,10 +164,14 @@ extension FileClient {
|
||||
|
||||
// MARK: - Private
|
||||
fileprivate func url(for path: String) throws -> URL {
|
||||
if #available(macOS 13.0, *) {
|
||||
return URL(filePath: path)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -101,6 +101,39 @@ 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.
|
||||
///
|
||||
/// > 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.
|
||||
/// - 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.
|
||||
@@ -115,17 +148,21 @@ extension ShellClient {
|
||||
_ closure: @escaping () 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)
|
||||
// grab the original contents, to set it back when done.
|
||||
let originalContents = try fileClient.readAsString(path: filePath)
|
||||
|
||||
let updatedContents = originalContents
|
||||
.replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"")
|
||||
try fileClient.write(string: updatedContents, to: filePath)
|
||||
defer { try! fileClient.write(string: originalContents, to: 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()
|
||||
}
|
||||
|
||||
try closure()
|
||||
}
|
||||
|
||||
/// Replace nil in the given file path and then build the project, using the
|
||||
|
||||
@@ -10,9 +10,20 @@ public struct Build {
|
||||
public static func main() throws {
|
||||
@Dependency(\.shellClient) var shell: ShellClient
|
||||
|
||||
try shell.replacingNilWithVersionString(
|
||||
in: "Sources/example/Version.swift",
|
||||
build: SwiftBuild.release()
|
||||
)
|
||||
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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ 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 {
|
||||
|
||||
|
||||
34
Sources/git-version-builder/GitVersionBuilder.swift
Normal file
34
Sources/git-version-builder/GitVersionBuilder.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ final class GitVersionTests: XCTestCase {
|
||||
$0.gitVersionClient.override(with: "blob")
|
||||
} operation: {
|
||||
@Dependency(\.gitVersionClient) var versionClient
|
||||
|
||||
|
||||
let version = try versionClient.currentVersion()
|
||||
XCTAssertEqual(version, "blob")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func test_live() throws {
|
||||
try withDependencies({
|
||||
$0.logger.logLevel = .debug
|
||||
@@ -22,18 +22,22 @@ final class GitVersionTests: XCTestCase {
|
||||
$0.shellClient = .liveValue
|
||||
$0.gitVersionClient = .liveValue
|
||||
}, operation: {
|
||||
|
||||
|
||||
@Dependency(\.gitVersionClient) var versionClient
|
||||
|
||||
|
||||
let gitDir = URL(fileURLWithPath: #file)
|
||||
.deletingLastPathComponent()
|
||||
.deletingLastPathComponent()
|
||||
.deletingLastPathComponent()
|
||||
|
||||
|
||||
let version = try versionClient.currentVersion(in: gitDir.absoluteString)
|
||||
print("VERSION: \(version)")
|
||||
// can't really have a predictable result for the live client.
|
||||
XCTAssertNotEqual(version, "blob")
|
||||
|
||||
|
||||
let other = try versionClient.currentVersion()
|
||||
XCTAssertEqual(version, other)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user