Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6927464d47 | ||
|
|
31ed16c828 | ||
|
|
ac1c86c431 | ||
|
|
4b28c293cb | ||
|
|
55f3ca2f7b |
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Generate Docs
|
- name: Generate Docs
|
||||||
run: |
|
run: |
|
||||||
swift package add-dependency --from 1.4.0 "https://github.com/apple/swift-docc-plugin.git"
|
swift package add-dependency --from 1.4.0 "https://github.com/apple/swift-docc-plugin.git"
|
||||||
for target in Inotify TaskCLI; do
|
for target in Inotify InotifyTaskCLI; do
|
||||||
lower="${target,,}"
|
lower="${target,,}"
|
||||||
mkdir -p "./public/$lower"
|
mkdir -p "./public/$lower"
|
||||||
swift package --allow-writing-to-directory "./public/$lower" \
|
swift package --allow-writing-to-directory "./public/$lower" \
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
cp ./.github/workflows/index.tpl.html public/index.html
|
cp ./.github/workflows/index.tpl.html public/index.html
|
||||||
sed -i -e 's/{{project.name}}/Swift Inotify/g' public/index.html
|
sed -i -e 's/{{project.name}}/Swift Inotify/g' public/index.html
|
||||||
sed -i -e 's/{{project.tagline}}/🗂️ Monitor filesystem events on Linux using modern Swift concurrency/g' public/index.html
|
sed -i -e 's/{{project.tagline}}/🗂️ Monitor filesystem events on Linux using modern Swift concurrency/g' public/index.html
|
||||||
sed -i -e 's|{{project.links}}|<li><a href="inotify/documentation/inotify/">Inotify</a>: The actual library.</li><li><a href="taskcli/documentation/taskcli/">TaskCLI</a>: The project build command.</li>|g' public/index.html
|
sed -i -e 's|{{project.links}}|<li><a href="inotify/documentation/inotify/">Inotify</a>: The actual library.</li><li><a href="inotifytaskcli/documentation/inotifytaskcli/">TaskCLI</a>: The project build command.</li>|g' public/index.html
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v4
|
uses: actions/upload-pages-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "fd1e824e418c767633bb79b055a4e84d9c86165746bc881d5d27457ad34b0c20",
|
"originHash" : "17ce26ba5c862ca674cd3ceeb43a9fe8a5c5251c5561de65e632a06d79916342",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "noora",
|
"identity" : "noora",
|
||||||
@@ -33,17 +33,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/apple/swift-argument-parser",
|
"location" : "https://github.com/apple/swift-argument-parser",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "c5d11a805e765f52ba34ec7284bd4fcd6ba68615",
|
"revision" : "626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b",
|
||||||
"version" : "1.7.0"
|
"version" : "1.7.1"
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-async-algorithms",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-async-algorithms",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "9d349bcc328ac3c31ce40e746b5882742a0d1272",
|
|
||||||
"version" : "1.1.3"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,15 +8,10 @@ let package = Package(
|
|||||||
.library(
|
.library(
|
||||||
name: "Inotify",
|
name: "Inotify",
|
||||||
targets: ["Inotify"]
|
targets: ["Inotify"]
|
||||||
),
|
|
||||||
.executable(
|
|
||||||
name: "task",
|
|
||||||
targets: ["TaskCLI"]
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.7.0"),
|
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.7.1"),
|
||||||
.package(url: "https://github.com/apple/swift-async-algorithms", from: "1.1.3"),
|
|
||||||
.package(url: "https://github.com/apple/swift-log", from: "1.10.1"),
|
.package(url: "https://github.com/apple/swift-log", from: "1.10.1"),
|
||||||
.package(url: "https://github.com/apple/swift-nio", from: "2.95.0"),
|
.package(url: "https://github.com/apple/swift-nio", from: "2.95.0"),
|
||||||
.package(url: "https://github.com/apple/swift-system", from: "1.6.4"),
|
.package(url: "https://github.com/apple/swift-system", from: "1.6.4"),
|
||||||
@@ -42,15 +37,15 @@ let package = Package(
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
name: "TaskCLI",
|
name: "InotifyTaskCLI",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||||
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
|
|
||||||
.product(name: "Logging", package: "swift-log"),
|
.product(name: "Logging", package: "swift-log"),
|
||||||
.product(name: "_NIOFileSystem", package: "swift-nio"),
|
.product(name: "_NIOFileSystem", package: "swift-nio"),
|
||||||
.product(name: "Subprocess", package: "swift-subprocess"),
|
.product(name: "Subprocess", package: "swift-subprocess"),
|
||||||
.product(name: "Noora", package: "Noora")
|
.product(name: "Noora", package: "Noora")
|
||||||
]
|
],
|
||||||
|
path: "Sources/TaskCLI"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -132,11 +132,14 @@ try inotify.removeWatch(wd)
|
|||||||
## Build Tool
|
## Build Tool
|
||||||
|
|
||||||
The package ships with a `task` executable (the `TaskCLI` target) that serves as the project's build tool. It automates running tests and generating documentation inside Linux Docker containers, so you can validate everything on the correct platform even when developing on macOS.
|
The package ships with a `task` executable (the `TaskCLI` target) that serves as the project's build tool. It automates running tests and generating documentation inside Linux Docker containers, so you can validate everything on the correct platform even when developing on macOS.
|
||||||
|
Because of a Swift Package Manager Bug in the [package dependency resolution][swiftpm-bug], the executable needs to be run using the `task.sh` shell script.
|
||||||
|
|
||||||
|
[swiftpm-bug]: https://github.com/swiftlang/swift-package-manager/issues/8482
|
||||||
|
|
||||||
### Tests
|
### Tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
swift run task test
|
./task.sh test
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `-v`, `-vv`, or `-vvv` to increase log verbosity. The command runs two passes: first all tests except `InotifyLimitTests`, then only `InotifyLimitTests` (which manipulate system-level inotify limits and need to run in isolation).
|
Use `-v`, `-vv`, or `-vvv` to increase log verbosity. The command runs two passes: first all tests except `InotifyLimitTests`, then only `InotifyLimitTests` (which manipulate system-level inotify limits and need to run in isolation).
|
||||||
@@ -148,7 +151,7 @@ Docker must be installed and running on your machine.
|
|||||||
Full API documentation is available as DocC catalogs bundled with the package. Generate them locally with:
|
Full API documentation is available as DocC catalogs bundled with the package. Generate them locally with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
swift run task generate-docs
|
./task.sh generate-docs
|
||||||
```
|
```
|
||||||
|
|
||||||
Then open the files in the newly created `public` folder.
|
Then open the files in the newly created `public` folder.
|
||||||
|
|||||||
@@ -3,27 +3,15 @@ import _NIOFileSystem
|
|||||||
public struct DoccFinder {
|
public struct DoccFinder {
|
||||||
static let fileManager = FileSystem.shared
|
static let fileManager = FileSystem.shared
|
||||||
|
|
||||||
public static func getTargetsWithDocumentation(at paths: String...) async throws -> [String] {
|
public static func hasDoccFolder(at path: String) async throws -> Bool {
|
||||||
try await Self.getTargetsWithDocumentation(at: paths)
|
let itemPath = FilePath(path)
|
||||||
}
|
var hasDoccFolder = false
|
||||||
|
|
||||||
static func getTargetsWithDocumentation(at paths: [String]) async throws -> [String] {
|
try await withSubdirectories(at: itemPath) { subdirectory in
|
||||||
var resolved: [String] = []
|
guard subdirectory.description.hasSuffix(".docc") else { return }
|
||||||
|
hasDoccFolder = true
|
||||||
for path in paths {
|
|
||||||
let itemPath = FilePath(path)
|
|
||||||
|
|
||||||
try await withSubdirectories(at: itemPath) { targetPath in
|
|
||||||
print("Target path is", targetPath.description)
|
|
||||||
try await withSubdirectories(at: targetPath) { subdirectory in
|
|
||||||
guard subdirectory.description.hasSuffix(".docc") else { return }
|
|
||||||
guard let target = targetPath.lastComponent?.description else { return }
|
|
||||||
resolved.append(target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return hasDoccFolder
|
||||||
return resolved
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func withSubdirectories(at path: FilePath, body: (FilePath) async throws -> Void) async throws {
|
private static func withSubdirectories(at path: FilePath, body: (FilePath) async throws -> Void) async throws {
|
||||||
|
|||||||
9
Sources/TaskCLI/Docker.swift
Normal file
9
Sources/TaskCLI/Docker.swift
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
struct Docker {
|
||||||
|
static func getLinuxPlatformStringWithHostArchitecture() -> String {
|
||||||
|
#if arch(x86_64)
|
||||||
|
return "linux/amd64"
|
||||||
|
#else
|
||||||
|
return "linux/arm64"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
import AsyncAlgorithms
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Logging
|
import Logging
|
||||||
import Noora
|
import Noora
|
||||||
@@ -43,28 +42,21 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
|
|||||||
let script = Self.makeRunScript(for: targets)
|
let script = Self.makeRunScript(for: targets)
|
||||||
|
|
||||||
logger.debug("Container script", metadata: ["script": "\(script)"])
|
logger.debug("Container script", metadata: ["script": "\(script)"])
|
||||||
let dockerResult = try await Subprocess.run(
|
let dockerRunResult = try await Subprocess.run(
|
||||||
.name("docker"),
|
.name("docker"),
|
||||||
arguments: [
|
arguments: [
|
||||||
"run", "--rm",
|
"run", "--rm",
|
||||||
"-v", "\(tempDirectory.path(percentEncoded: false)):/code",
|
"-v", "\(tempDirectory.path):/code",
|
||||||
"--platform", "linux/arm64",
|
"-v", "swift-inotify-build-cache:/code/.build",
|
||||||
|
"--platform", Docker.getLinuxPlatformStringWithHostArchitecture(),
|
||||||
"-w", "/code",
|
"-w", "/code",
|
||||||
"swift:latest",
|
"swift:latest",
|
||||||
"/bin/bash", "-c", script,
|
"/bin/bash", "-c", script
|
||||||
],
|
],
|
||||||
preferredBufferSize: 10,
|
output: .standardOutput,
|
||||||
) { execution, standardInput, standardOutput, standardError in
|
error: .standardError
|
||||||
print("")
|
)
|
||||||
let stdout = standardOutput.lines()
|
if !dockerRunResult.terminationStatus.isSuccess {
|
||||||
let stderr = standardError.lines()
|
|
||||||
for try await line in merge(stdout, stderr) {
|
|
||||||
noora.passthrough("\(line)")
|
|
||||||
}
|
|
||||||
print("")
|
|
||||||
}
|
|
||||||
|
|
||||||
guard dockerResult.terminationStatus.isSuccess else {
|
|
||||||
noora.error("Documentation generation failed.")
|
noora.error("Documentation generation failed.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -90,7 +82,7 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
|
|||||||
("{{project.tagline}}", "🗂️ Monitor filesystem events on Linux using modern Swift concurrency"),
|
("{{project.tagline}}", "🗂️ Monitor filesystem events on Linux using modern Swift concurrency"),
|
||||||
("{{project.links}}", """
|
("{{project.links}}", """
|
||||||
<li><a href="inotify/documentation/inotify/">Inotify</a>: The actual library.</li>\
|
<li><a href="inotify/documentation/inotify/">Inotify</a>: The actual library.</li>\
|
||||||
<li><a href="taskcli/documentation/taskcli/">TaskCLI</a>: The project build command.</li>
|
<li><a href="inotifytaskcli/documentation/inotifytaskcli/">TaskCLI</a>: The project build command.</li>
|
||||||
"""),
|
"""),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -106,9 +98,37 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static func targets(for projectDirectory: URL) async throws -> [String] {
|
private static func targets(for projectDirectory: URL) async throws -> [String] {
|
||||||
let sourcesDirectory = projectDirectory.appending(path: "Sources").path
|
let packages = try await Self.packageTargets()
|
||||||
let testsDirectory = projectDirectory.appending(path: "Tests").path
|
var packagesWithDoccFolder: [(name: String, path: String)] = []
|
||||||
return try await DoccFinder.getTargetsWithDocumentation(at: sourcesDirectory, testsDirectory)
|
for package in packages {
|
||||||
|
guard try await DoccFinder.hasDoccFolder(at: package.path) else { continue }
|
||||||
|
packagesWithDoccFolder.append(package)
|
||||||
|
}
|
||||||
|
return packagesWithDoccFolder.map { $0.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func packageTargets() async throws -> [(name: String, path: String)] {
|
||||||
|
let packageDescriptionResult = try await Subprocess.run(
|
||||||
|
.name("swift"),
|
||||||
|
arguments: ["package", "describe", "--type", "json"],
|
||||||
|
output: .data(limit: 10_000),
|
||||||
|
error: .standardError
|
||||||
|
)
|
||||||
|
|
||||||
|
struct PackageDescription: Codable {
|
||||||
|
let targets: [Target]
|
||||||
|
}
|
||||||
|
struct Target: Codable {
|
||||||
|
let name: String
|
||||||
|
let path: String
|
||||||
|
}
|
||||||
|
|
||||||
|
if !packageDescriptionResult.terminationStatus.isSuccess {
|
||||||
|
throw GenerateDocumentationError.unableToReadPackageDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
let package = try JSONDecoder().decode(PackageDescription.self, from: packageDescriptionResult.standardOutput)
|
||||||
|
return package.targets.map { ($0.name, $0.path) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func makeRunScript(for targets: [String]) -> String {
|
private static func makeRunScript(for targets: [String]) -> String {
|
||||||
@@ -152,15 +172,16 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
|
|||||||
// MARK: - Dependency Injection
|
// MARK: - Dependency Injection
|
||||||
|
|
||||||
private func injectDoccPluginDependency(in directory: URL, logger: Logger) async throws {
|
private func injectDoccPluginDependency(in directory: URL, logger: Logger) async throws {
|
||||||
let result = try await Subprocess.run(
|
let swiftRunResult = try await Subprocess.run(
|
||||||
.name("swift"),
|
.name("swift"),
|
||||||
arguments: [
|
arguments: [
|
||||||
"package", "--package-path", directory.path(percentEncoded: false),
|
"package", "--package-path", directory.path(percentEncoded: false),
|
||||||
"add-dependency", "--from", Self.doccPluginMinVersion, Self.doccPluginURL
|
"add-dependency", "--from", Self.doccPluginMinVersion, Self.doccPluginURL
|
||||||
],
|
],
|
||||||
) { _ in }
|
output: .standardOutput,
|
||||||
|
error: .standardError
|
||||||
guard result.terminationStatus.isSuccess else {
|
)
|
||||||
|
if !swiftRunResult.terminationStatus.isSuccess {
|
||||||
throw GenerateDocumentationError.dependencyInjectionFailed
|
throw GenerateDocumentationError.dependencyInjectionFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,11 +191,14 @@ struct GenerateDocumentationCommand: AsyncParsableCommand {
|
|||||||
|
|
||||||
enum GenerateDocumentationError: Error, CustomStringConvertible {
|
enum GenerateDocumentationError: Error, CustomStringConvertible {
|
||||||
case dependencyInjectionFailed
|
case dependencyInjectionFailed
|
||||||
|
case unableToReadPackageDescription
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .dependencyInjectionFailed:
|
case .dependencyInjectionFailed:
|
||||||
"Failed to add swift-docc-plugin dependency to Package.swift."
|
"Failed to add swift-docc-plugin dependency to Package.swift."
|
||||||
|
case .unableToReadPackageDescription:
|
||||||
|
"Failed to read the package description."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ``TaskCLI``
|
# ``InotifyTaskCLI``
|
||||||
|
|
||||||
The build tool for the Swift Inotify project.
|
The build tool for the Swift Inotify project.
|
||||||
|
|
||||||
@@ -6,10 +6,14 @@ The build tool for the Swift Inotify project.
|
|||||||
|
|
||||||
`TaskCLI` is a small command-line executable (exposed as `task` in `Package.swift`) that automates project-level workflows. Its primary purpose is running integration tests and generating documentation inside Linux Docker containers, so you can validate inotify-dependent code on the correct platform even when developing on macOS.
|
`TaskCLI` is a small command-line executable (exposed as `task` in `Package.swift`) that automates project-level workflows. Its primary purpose is running integration tests and generating documentation inside Linux Docker containers, so you can validate inotify-dependent code on the correct platform even when developing on macOS.
|
||||||
|
|
||||||
|
Because of a Swift Package Manager Bug in the [package dependency resolution][swiftpm-bug], the executable needs to be run using the `task.sh` shell script.
|
||||||
|
|
||||||
|
[swiftpm-bug]: https://github.com/swiftlang/swift-package-manager/issues/8482
|
||||||
|
|
||||||
### Running the Tests
|
### Running the Tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
swift run task test
|
./task.sh test
|
||||||
```
|
```
|
||||||
|
|
||||||
This launches a `swift:latest` Docker container with the repository mounted at `/code`, then executes two test passes:
|
This launches a `swift:latest` Docker container with the repository mounted at `/code`, then executes two test passes:
|
||||||
@@ -22,7 +26,7 @@ The container is started with `--security-opt systempaths=unconfined` so that th
|
|||||||
### Generating Documentation
|
### Generating Documentation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
swift run task generate-documentation
|
./task.sh generate-documentation
|
||||||
```
|
```
|
||||||
|
|
||||||
This copies the project to a temporary directory, injects the `swift-docc-plugin` dependency via `swift package add-dependency` (if absent), and runs documentation generation inside a `swift:latest` Docker container. The resulting static sites are written to `./public/inotify/` and `./public/taskcli/`, ready for deployment to GitHub Pages.
|
This copies the project to a temporary directory, injects the `swift-docc-plugin` dependency via `swift package add-dependency` (if absent), and runs documentation generation inside a `swift:latest` Docker container. The resulting static sites are written to `./public/inotify/` and `./public/taskcli/`, ready for deployment to GitHub Pages.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
import AsyncAlgorithms
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Subprocess
|
|
||||||
import Noora
|
import Noora
|
||||||
|
import Subprocess
|
||||||
|
|
||||||
struct TestCommand: AsyncParsableCommand {
|
struct TestCommand: AsyncParsableCommand {
|
||||||
static let configuration = CommandConfiguration(
|
static let configuration = CommandConfiguration(
|
||||||
@@ -22,21 +21,21 @@ struct TestCommand: AsyncParsableCommand {
|
|||||||
|
|
||||||
noora.info("Running tests on Linux.")
|
noora.info("Running tests on Linux.")
|
||||||
logger.debug("Current directory", metadata: ["current-directory": "\(currentDirectory)"])
|
logger.debug("Current directory", metadata: ["current-directory": "\(currentDirectory)"])
|
||||||
async let monitorResult = Subprocess.run(
|
let dockerRunResult = try await Subprocess.run(
|
||||||
.name("docker"),
|
.name("docker"),
|
||||||
arguments: ["run", "-v", "\(currentDirectory):/code", "--security-opt", "systempaths=unconfined", "--platform", "linux/arm64", "-w", "/code", "swift:latest", "/bin/bash", "-c", "swift test --skip InotifyLimitTests; swift test --skip-build --filter InotifyLimitTests"],
|
arguments: [
|
||||||
preferredBufferSize: 10,
|
"run",
|
||||||
) { execution, standardInput, standardOutput, standardError in
|
"-v", "\(currentDirectory):/code",
|
||||||
print("")
|
"-v", "swift-inotify-build-cache:/code/.build",
|
||||||
let stdout = standardOutput.lines()
|
"--security-opt", "systempaths=unconfined",
|
||||||
let stderr = standardError.lines()
|
"--platform", Docker.getLinuxPlatformStringWithHostArchitecture(),
|
||||||
for try await line in merge(stdout, stderr) {
|
"-w", "/code", "swift:latest",
|
||||||
noora.passthrough("\(line)")
|
"/bin/bash", "-c", "swift test --skip InotifyLimitTests; swift test --skip-build --filter InotifyLimitTests"
|
||||||
}
|
],
|
||||||
print("")
|
output: .standardOutput,
|
||||||
}
|
error: .standardError
|
||||||
|
)
|
||||||
if (try await monitorResult.terminationStatus.isSuccess) {
|
if dockerRunResult.terminationStatus.isSuccess {
|
||||||
noora.success("All tests completed successfully.")
|
noora.success("All tests completed successfully.")
|
||||||
} else {
|
} else {
|
||||||
noora.error("Not all tests completed successfully.")
|
noora.error("Not all tests completed successfully.")
|
||||||
|
|||||||
58
task.sh
Executable file
58
task.sh
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
|
# task - Run the package's TaskCLI target via a transient "task" product.
|
||||||
|
#
|
||||||
|
# Works around https://github.com/swiftlang/swift-package-manager/issues/8482
|
||||||
|
# by temporarily adding an executable product named "task" to Package.swift,
|
||||||
|
# running it with `swift run`, and restoring the original manifest afterwards.
|
||||||
|
#
|
||||||
|
# Usage: task [arguments...]
|
||||||
|
#
|
||||||
|
# The script auto-detects the package name from Package.swift and expects an
|
||||||
|
# executable target named "<PackageName>TaskCLI" to exist.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# --- Resolve the package root (search upward for Package.swift) -----------
|
||||||
|
|
||||||
|
package_root="${PWD}"
|
||||||
|
while [[ ! -f "${package_root}/Package.swift" ]]; do
|
||||||
|
package_root="${package_root:h}" # zsh dirname
|
||||||
|
if [[ "${package_root}" == "/" ]]; then
|
||||||
|
echo "error: Could not find Package.swift in any parent directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
manifest="${package_root}/Package.swift"
|
||||||
|
backup="${manifest}.task-backup"
|
||||||
|
|
||||||
|
# --- Extract the package name ---------------------------------------------
|
||||||
|
|
||||||
|
package_name=$(sed -n 's/^.*name:[[:space:]]*"\([^"]*\)".*/\1/p' "${manifest}" | head -1)
|
||||||
|
if [[ -z "${package_name}" ]]; then
|
||||||
|
echo "error: Could not determine package name from ${manifest}." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
target_name="${package_name}TaskCLI"
|
||||||
|
|
||||||
|
# --- Cleanup trap (runs on EXIT — covers success, failure, signals) -------
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
if [[ -f "${backup}" ]]; then
|
||||||
|
mv -f "${backup}" "${manifest}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# --- Inject the transient "task" product ----------------------------------
|
||||||
|
|
||||||
|
cp -f "${manifest}" "${backup}"
|
||||||
|
|
||||||
|
swift package --package-path "${package_root}" \
|
||||||
|
add-product task --type executable --targets "${target_name}"
|
||||||
|
|
||||||
|
# --- Run it (forward all script arguments) --------------------------------
|
||||||
|
|
||||||
|
swift run --package-path "${package_root}" task "$@"
|
||||||
Reference in New Issue
Block a user