wip
This commit is contained in:
@@ -18,6 +18,15 @@
|
|||||||
"version" : "4.0.1"
|
"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",
|
"identity" : "swift-clocks",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -36,6 +45,24 @@
|
|||||||
"version" : "0.2.0"
|
"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",
|
"identity" : "swift-log",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
18
Makefile
18
Makefile
@@ -2,10 +2,13 @@ DOCC_TARGET ?= GitVersion
|
|||||||
DOCC_BASEPATH = $(shell basename "$(PWD)")
|
DOCC_BASEPATH = $(shell basename "$(PWD)")
|
||||||
DOCC_DIR ?= ./docs
|
DOCC_DIR ?= ./docs
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf .build
|
||||||
|
|
||||||
build-and-run:
|
build-and-run:
|
||||||
swift run build-example
|
swift run -c release build-example
|
||||||
./.build/debug/example --help
|
./.build/release/example --help
|
||||||
./.build/debug/example
|
./.build/release/example
|
||||||
|
|
||||||
build-documentation:
|
build-documentation:
|
||||||
swift package \
|
swift package \
|
||||||
@@ -22,3 +25,12 @@ preview-documentation:
|
|||||||
--disable-sandbox \
|
--disable-sandbox \
|
||||||
preview-documentation \
|
preview-documentation \
|
||||||
--target "$(DOCC_TARGET)"
|
--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(
|
let package = Package(
|
||||||
name: "swift-git-version",
|
name: "swift-git-version",
|
||||||
platforms: [
|
platforms: [
|
||||||
.macOS(.v10_15)
|
.macOS(.v12)
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(name: "GitVersion", targets: ["GitVersion"])
|
.library(name: "GitVersion", targets: ["GitVersion"]),
|
||||||
|
.plugin(name: "GitVersionBuildPlugin", targets: ["GitVersionBuildPlugin"]),
|
||||||
],
|
],
|
||||||
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"),
|
||||||
@@ -16,6 +17,13 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2"),
|
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2"),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
|
.executableTarget(
|
||||||
|
name: "git-version-builder",
|
||||||
|
dependencies: [
|
||||||
|
"GitVersion",
|
||||||
|
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
||||||
|
]
|
||||||
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "GitVersion",
|
name: "GitVersion",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
@@ -34,10 +42,33 @@ let package = Package(
|
|||||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||||
.product(name: "ShellClient", package: "swift-shell-client")
|
.product(name: "ShellClient", package: "swift-shell-client")
|
||||||
]
|
]
|
||||||
|
,
|
||||||
|
plugins: [
|
||||||
|
.plugin(name: "GitVersionBuildPlugin")
|
||||||
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "GitVersionTests",
|
name: "GitVersionTests",
|
||||||
dependencies: ["GitVersion"]
|
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
|
# 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
|
// MARK: - Private
|
||||||
fileprivate func url(for path: String) throws -> URL {
|
fileprivate func url(for path: String) throws -> URL {
|
||||||
if #available(macOS 13.0, *) {
|
#if os(Linux)
|
||||||
return URL(filePath: path)
|
|
||||||
} else {
|
|
||||||
// Fallback on earlier versions
|
|
||||||
return URL(fileURLWithPath: path)
|
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 {
|
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.
|
/// 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.
|
/// > Note: The file contents will be reset back to nil after the closure operation.
|
||||||
@@ -115,17 +148,21 @@ extension ShellClient {
|
|||||||
_ closure: @escaping () throws -> Void
|
_ closure: @escaping () throws -> Void
|
||||||
) throws {
|
) throws {
|
||||||
@Dependency(\.fileClient) var fileClient: FileClient
|
@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 originalContents = try fileClient.readAsString(path: filePath)
|
||||||
|
|
||||||
let updatedContents = originalContents
|
try self.replacingNilWithVersionString(
|
||||||
.replacingOccurrences(of: "nil", with: "\"\(currentVersion)\"")
|
in: filePath,
|
||||||
try fileClient.write(string: updatedContents, to: filePath)
|
from: workingDirectory
|
||||||
defer { try! fileClient.write(string: originalContents, to: filePath) }
|
) { 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
|
/// 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 {
|
public static func main() throws {
|
||||||
@Dependency(\.shellClient) var shell: ShellClient
|
@Dependency(\.shellClient) var shell: ShellClient
|
||||||
|
|
||||||
try shell.replacingNilWithVersionString(
|
let gitDir = URL(fileURLWithPath: #file)
|
||||||
in: "Sources/example/Version.swift",
|
.deletingLastPathComponent()
|
||||||
build: SwiftBuild.release()
|
.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
|
/// An example of using the git version client with a command line tool
|
||||||
/// The ``VERSION`` variable get's set during the build process.
|
/// The ``VERSION`` variable get's set during the build process.
|
||||||
|
|
||||||
@main
|
@main
|
||||||
public struct Example: ParsableCommand {
|
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")
|
$0.gitVersionClient.override(with: "blob")
|
||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.gitVersionClient) var versionClient
|
@Dependency(\.gitVersionClient) var versionClient
|
||||||
|
|
||||||
let version = try versionClient.currentVersion()
|
let version = try versionClient.currentVersion()
|
||||||
XCTAssertEqual(version, "blob")
|
XCTAssertEqual(version, "blob")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_live() throws {
|
func test_live() throws {
|
||||||
try withDependencies({
|
try withDependencies({
|
||||||
$0.logger.logLevel = .debug
|
$0.logger.logLevel = .debug
|
||||||
@@ -22,18 +22,22 @@ final class GitVersionTests: XCTestCase {
|
|||||||
$0.shellClient = .liveValue
|
$0.shellClient = .liveValue
|
||||||
$0.gitVersionClient = .liveValue
|
$0.gitVersionClient = .liveValue
|
||||||
}, operation: {
|
}, operation: {
|
||||||
|
|
||||||
@Dependency(\.gitVersionClient) var versionClient
|
@Dependency(\.gitVersionClient) var versionClient
|
||||||
|
|
||||||
let gitDir = URL(fileURLWithPath: #file)
|
let gitDir = URL(fileURLWithPath: #file)
|
||||||
.deletingLastPathComponent()
|
.deletingLastPathComponent()
|
||||||
.deletingLastPathComponent()
|
.deletingLastPathComponent()
|
||||||
.deletingLastPathComponent()
|
.deletingLastPathComponent()
|
||||||
|
|
||||||
let version = try versionClient.currentVersion(in: gitDir.absoluteString)
|
let version = try versionClient.currentVersion(in: gitDir.absoluteString)
|
||||||
|
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")
|
||||||
|
|
||||||
|
let other = try versionClient.currentVersion()
|
||||||
|
XCTAssertEqual(version, other)
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user