Skip to content

Commit

Permalink
Merge pull request #1280 from Quick/quicklint
Browse files Browse the repository at this point in the history
Add QuickLint, a CLI tool to detect focused specs, and optionally remove the focus.
  • Loading branch information
younata committed May 12, 2024
2 parents 46c0056 + 105b82f commit f42e554
Show file tree
Hide file tree
Showing 27 changed files with 1,483 additions and 7 deletions.
48 changes: 42 additions & 6 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,62 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/mattgallagher/CwlCatchException.git",
"state" : {
"revision" : "3b123999de19bf04905bc1dfdb76f817b0f2cc00",
"version" : "2.1.2"
"revision" : "3ef6999c73b6938cc0da422f2c912d0158abb0a0",
"version" : "2.2.0"
}
},
{
"identity" : "cwlpreconditiontesting",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git",
"state" : {
"revision" : "dc9af4781f2afdd1e68e90f80b8603be73ea7abc",
"version" : "2.2.0"
"revision" : "2ef56b2caf25f55fa7eef8784c30d5a767550f54",
"version" : "2.2.1"
}
},
{
"identity" : "nimble",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Quick/Nimble.git",
"state" : {
"revision" : "c1f3dd66222d5e7a1a20afc237f7e7bc432c564f",
"version" : "13.2.0"
"revision" : "efe11bbca024b57115260709b5c05e01131470d0",
"version" : "13.2.1"
}
},
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-algorithms.git",
"state" : {
"revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
"version" : "1.2.0"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "46989693916f56d1186bd59ac15124caef896560",
"version" : "1.3.1"
}
},
{
"identity" : "swift-fakes",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Quick/swift-fakes.git",
"state" : {
"revision" : "d38a57c30ec2cbc7fb1c69e20fe3d7b7aebd60b3",
"version" : "0.0.1"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics.git",
"state" : {
"revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
"version" : "1.0.2"
}
}
],
Expand Down
46 changes: 45 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,47 @@ let package = Package(
],
products: [
.library(name: "Quick", targets: ["Quick"]),
.executable(name: "quicklint", targets: ["QuickLint"]),
.plugin(name: "DefocusCommandPlugin", targets: ["DefocusCommandPlugin"]),
.plugin(name: "LintError", targets: ["LintError"]),
.plugin(name: "LintWarning", targets: ["LintWarning"]),
.plugin(name: "LintCommandPlugin", targets: ["LintCommandPlugin"]),
],
dependencies: [
.package(url: "https://github.com/Quick/Nimble.git", from: "12.0.0"),
.package(url: "https://github.com/Quick/Nimble.git", from: "13.2.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.1"),
.package(url: "https://github.com/apple/swift-algorithms.git", from: "1.2.0"),
.package(url: "https://github.com/Quick/swift-fakes.git", from: "0.0.1"),
],
targets: {
var targets: [Target] = [
.plugin(
name: "DefocusCommandPlugin",
capability: .command(intent: .sourceCodeFormatting(), permissions: [.writeToPackageDirectory(reason: "Remove focus from Quick tests")]),
dependencies: ["QuickLint"]
),
.plugin(
name: "LintError",
capability: .buildTool(),
dependencies: ["QuickLint"]
),
.plugin(
name: "LintWarning",
capability: .buildTool(),
dependencies: ["QuickLint"]
),
.plugin(
name: "LintCommandPlugin",
capability: .command(intent: .custom(verb: "quicklint", description: "Verify no focused tests in Quick tests"), permissions: []),
dependencies: ["QuickLint"]
),
.executableTarget(
name: "QuickLint",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Algorithms", package: "swift-algorithms"),
]
),
.testTarget(
name: "QuickTests",
dependencies: [ "Quick", "Nimble" ],
Expand All @@ -35,6 +70,15 @@ let package = Package(
name: "QuickIssue853RegressionTests",
dependencies: [ "Quick" ]
),
.testTarget(
name: "QuickLintTests",
dependencies: [
"QuickLint",
"Quick",
"Nimble",
.product(name: "Fakes", package: "swift-fakes"),
]
)
]
#if os(macOS)
targets.append(contentsOf: [
Expand Down
44 changes: 44 additions & 0 deletions Package@swift-5.9.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,47 @@ let package = Package(
],
products: [
.library(name: "Quick", targets: ["Quick"]),
.executable(name: "quicklint", targets: ["QuickLint"]),
.plugin(name: "DefocusCommandPlugin", targets: ["DefocusCommandPlugin"]),
.plugin(name: "LintError", targets: ["LintError"]),
.plugin(name: "LintWarning", targets: ["LintWarning"]),
.plugin(name: "LintCommandPlugin", targets: ["LintCommandPlugin"]),
],
dependencies: [
.package(url: "https://github.com/Quick/Nimble.git", from: "13.2.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.1"),
.package(url: "https://github.com/apple/swift-algorithms.git", from: "1.2.0"),
.package(url: "https://github.com/Quick/swift-fakes.git", from: "0.0.1"),
],
targets: {
var targets: [Target] = [
.plugin(
name: "DefocusCommandPlugin",
capability: .command(intent: .sourceCodeFormatting(), permissions: [.writeToPackageDirectory(reason: "Remove focus from Quick tests")]),
dependencies: ["QuickLint"]
),
.plugin(
name: "LintError",
capability: .buildTool(),
dependencies: ["QuickLint"]
),
.plugin(
name: "LintWarning",
capability: .buildTool(),
dependencies: ["QuickLint"]
),
.plugin(
name: "LintCommandPlugin",
capability: .command(intent: .custom(verb: "quicklint", description: "Verify no focused tests in Quick tests"), permissions: []),
dependencies: ["QuickLint"]
),
.executableTarget(
name: "QuickLint",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Algorithms", package: "swift-algorithms"),
]
),
.testTarget(
name: "QuickTests",
dependencies: [ "Quick", "Nimble" ],
Expand All @@ -35,6 +70,15 @@ let package = Package(
name: "QuickIssue853RegressionTests",
dependencies: [ "Quick" ]
),
.testTarget(
name: "QuickLintTests",
dependencies: [
"QuickLint",
"Quick",
"Nimble",
.product(name: "Fakes", package: "swift-fakes"),
]
)
]
#if os(macOS)
targets.append(contentsOf: [
Expand Down
57 changes: 57 additions & 0 deletions Plugins/DefocusCommandPlugin/DefocusCommandPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Foundation
import PackagePlugin

@main
struct DefocusCommandPlugin: CommandPlugin {
func performCommand(
context: PluginContext,
arguments: [String]
) throws {
try run(
tool: try context.tool(named: "QuickLint"),
workingDirectory: URL(fileURLWithPath: context.package.directory.string),
arguments: arguments
)
}

private func run(
tool: PluginContext.Tool,
workingDirectory: URL,
arguments: [String]
) throws {
let process: Process = .init()
process.currentDirectoryURL = workingDirectory
process.executableURL = URL(fileURLWithPath: tool.path.string)
process.arguments = ["defocus"] + arguments
try process.run()
process.waitUntilExit()
switch process.terminationReason {
case .exit:
break
case .uncaughtSignal:
Diagnostics.error("Uncaught Signal")
@unknown default:
Diagnostics.error("Unexpected Termination Reason")
}
guard process.terminationStatus == EXIT_SUCCESS else {
Diagnostics.error("Command Failed")
return
}
}
}

#if canImport(XcodeProjectPlugin)

import XcodeProjectPlugin

extension DefocusCommandPlugin: XcodeCommandPlugin {
func performCommand(context: XcodePluginContext, arguments: [String]) throws {
try run(
tool: try context.tool(named: "QuickLint"),
workingDirectory: URL(fileURLWithPath: context.pluginWorkDirectory.string),
arguments: arguments
)
}
}

#endif
57 changes: 57 additions & 0 deletions Plugins/LintCommandPlugin/LintCommandPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Foundation
import PackagePlugin

@main
struct LintCommandPlugin: CommandPlugin {
func performCommand(
context: PluginContext,
arguments: [String]
) throws {
try run(
tool: try context.tool(named: "QuickLint"),
workingDirectory: URL(fileURLWithPath: context.package.directory.string),
arguments: arguments
)
}

private func run(
tool: PluginContext.Tool,
workingDirectory: URL,
arguments: [String]
) throws {
let process: Process = .init()
process.currentDirectoryURL = workingDirectory
process.executableURL = URL(fileURLWithPath: tool.path.string)
process.arguments = ["lint"] + arguments
try process.run()
process.waitUntilExit()
switch process.terminationReason {
case .exit:
break
case .uncaughtSignal:
Diagnostics.error("Uncaught Signal")
@unknown default:
Diagnostics.error("Unexpected Termination Reason")
}
guard process.terminationStatus == EXIT_SUCCESS else {
Diagnostics.error("Command Failed")
return
}
}
}

#if canImport(XcodeProjectPlugin)

import XcodeProjectPlugin

extension LintCommandPlugin: XcodeCommandPlugin {
func performCommand(context: XcodePluginContext, arguments: [String]) throws {
try run(
tool: try context.tool(named: "QuickLint"),
workingDirectory: URL(fileURLWithPath: context.pluginWorkDirectory.string),
arguments: arguments
)
}
}

#endif
65 changes: 65 additions & 0 deletions Plugins/LintError/LintError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Foundation
import PackagePlugin

@main
struct LintError: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: any Target) async throws -> [Command] {
try makeCommand(
executable: context.tool(named: "QuickLint"),
files: (target as? SourceModuleTarget).flatMap(lintableFiles(target:)) ?? [],
pluginWorkDirectory: context.pluginWorkDirectory
)
}

private func lintableFiles(target: SourceModuleTarget) -> [Path] {
target
.sourceFiles
.filter(isLintable(file:))
.map { $0.path }
}

private func isLintable(file: PackagePlugin.File) -> Bool {
return ["swift", "m", "mm"].contains(file.path.extension ?? "")
}

private func makeCommand(
executable: PluginContext.Tool,
files: [Path],
pluginWorkDirectory path: Path) throws -> [Command] {
guard files.isEmpty == false else { return [] }

return [Command.buildCommand(
displayName: "quicklint",
executable: executable.path,
arguments: ["lint", "--error"] + files.map { $0.string },
inputFiles: files,
outputFiles: files
)]
}
}

#if canImport(XcodeProjectPlugin)

import XcodeProjectPlugin

extension LintError: XcodeBuildToolPlugin {
func createBuildCommands(
context: XcodePluginContext,
target: XcodeTarget
) throws -> [Command] {
try makeCommand(
executable: context.tool(named: "QuickLint"),
files: lintableFiles(target: target),
pluginWorkDirectory: context.pluginWorkDirectory
)
}

private func lintableFiles(target: XcodeTarget) -> [Path] {
target.inputFiles
.filter(isLintable(file:))
.map { $0.path }
}

}

#endif

0 comments on commit f42e554

Please sign in to comment.