Add a `reporters` subcommand (#4836)

This commit is contained in:
Martin Redington 2023-03-31 07:09:01 +01:00 committed by GitHub
parent ca43d2359b
commit 7dad240ea7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 104 additions and 107 deletions

View File

@ -0,0 +1,7 @@
/// The reporters list containing all the reporters built into SwiftLint.
public let reportersList: [Reporter.Type] = [
{% for reporter in types.structs where reporter.name|hasSuffix:"Reporter" %}
{{ reporter.name }}.self{% if not forloop.last %},{% endif %}
{% endfor %}
]

View File

@ -133,6 +133,11 @@
of each rule in a text table.
[Martin Redington](https://github.com/mildm8nnered)
* Adds a new `reporters` subcommand, to improve discoverability of
reporters.
[Martin Redington](https://github.com/mildm8nnered)
[#4819](https://github.com/realm/SwiftLint/issues/4819)
#### Bug Fixes
* Report violations in all `<scope>_length` rules when the error threshold is

View File

@ -30,12 +30,16 @@ VERSION_STRING=$(shell ./tools/get-version)
all: build
sourcery: Source/SwiftLintFramework/Models/PrimaryRuleList.swift Tests/GeneratedTests/GeneratedTests.swift
sourcery: Source/SwiftLintFramework/Models/PrimaryRuleList.swift Source/SwiftLintFramework/Models/ReportersList.swift Tests/GeneratedTests/GeneratedTests.swift
Source/SwiftLintFramework/Models/PrimaryRuleList.swift: Source/SwiftLintFramework/Rules/**/*.swift .sourcery/PrimaryRuleList.stencil
sourcery --sources Source/SwiftLintFramework/Rules --templates .sourcery/PrimaryRuleList.stencil --output .sourcery
mv .sourcery/PrimaryRuleList.generated.swift Source/SwiftLintFramework/Models/PrimaryRuleList.swift
Source/SwiftLintFramework/Models/ReportersList.swift: Source/SwiftLintFramework/Reporters/*.swift .sourcery/ReportersList.stencil
sourcery --sources Source/SwiftLintFramework/Reporters --templates .sourcery/ReportersList.stencil --output .sourcery
mv .sourcery/ReportersList.generated.swift Source/SwiftLintFramework/Models/ReportersList.swift
Tests/GeneratedTests/GeneratedTests.swift: Source/SwiftLintFramework/Rules/**/*.swift .sourcery/GeneratedTests.stencil
sourcery --sources Source/SwiftLintFramework/Rules --templates .sourcery/GeneratedTests.stencil --output .sourcery
mv .sourcery/GeneratedTests.generated.swift Tests/GeneratedTests/GeneratedTests.swift

View File

@ -313,6 +313,7 @@ SUBCOMMANDS:
docs Open SwiftLint documentation website in the default web browser
generate-docs Generates markdown documentation for all rules
lint (default) Print lint warnings and errors
reporters Display the list of reporters and their identifiers
rules Display the list of rules and their identifiers
version Display the current version of SwiftLint
@ -554,7 +555,7 @@ identifier_name:
- id
- URL
- GlobalAPIKey
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging)
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging, summary)
```
You can also use environment variables in your configuration file,

View File

@ -1,4 +1,4 @@
// Generated using Sourcery 2.0.0 https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 2.0.1 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
/// The rule list containing all available rules built into SwiftLint.

View File

@ -0,0 +1,20 @@
// Generated using Sourcery 2.0.1 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
/// The reporters list containing all the reporters built into SwiftLint.
public let reportersList: [Reporter.Type] = [
CSVReporter.self,
CheckstyleReporter.self,
CodeClimateReporter.self,
EmojiReporter.self,
GitHubActionsLoggingReporter.self,
GitLabJUnitReporter.self,
HTMLReporter.self,
JSONReporter.self,
JUnitReporter.self,
MarkdownReporter.self,
RelativePathReporter.self,
SonarQubeReporter.self,
SummaryReporter.self,
XcodeReporter.self
]

View File

@ -7,6 +7,12 @@ public protocol Reporter: CustomStringConvertible {
/// collected before generating the report.
static var isRealtime: Bool { get }
/// A more detailed description of the reporter's output.
static var description: String { get }
/// For CustomStringConvertible conformance.
var description: String { get }
/// Return a string with the report for the specified violations.
///
/// - parameter violations: The violations to report.
@ -15,43 +21,20 @@ public protocol Reporter: CustomStringConvertible {
static func generateReport(_ violations: [StyleViolation]) -> String
}
public extension Reporter {
/// For CustomStringConvertible conformance.
var description: String { Self.description }
}
/// Returns the reporter with the specified identifier. Traps if the specified identifier doesn't correspond to any
/// known reporters.
///
/// - parameter identifier: The identifier corresponding to the reporter.
///
/// - returns: The reporter type.
public func reporterFrom(identifier: String) -> Reporter.Type { // swiftlint:disable:this cyclomatic_complexity
switch identifier {
case XcodeReporter.identifier:
return XcodeReporter.self
case JSONReporter.identifier:
return JSONReporter.self
case CSVReporter.identifier:
return CSVReporter.self
case CheckstyleReporter.identifier:
return CheckstyleReporter.self
case JUnitReporter.identifier:
return JUnitReporter.self
case HTMLReporter.identifier:
return HTMLReporter.self
case EmojiReporter.identifier:
return EmojiReporter.self
case SonarQubeReporter.identifier:
return SonarQubeReporter.self
case MarkdownReporter.identifier:
return MarkdownReporter.self
case GitHubActionsLoggingReporter.identifier:
return GitHubActionsLoggingReporter.self
case GitLabJUnitReporter.identifier:
return GitLabJUnitReporter.self
case CodeClimateReporter.identifier:
return CodeClimateReporter.self
case RelativePathReporter.identifier:
return RelativePathReporter.self
case SummaryReporter.identifier:
return SummaryReporter.self
default:
public func reporterFrom(identifier: String) -> Reporter.Type {
guard let reporter = reportersList.first(where: { $0.identifier == identifier }) else {
queuedFatalError("no reporter with identifier '\(identifier)' available.")
}
return reporter
}

View File

@ -6,10 +6,7 @@ public struct CSVReporter: Reporter {
public static let identifier = "csv"
public static let isRealtime = false
public var description: String {
return "Reports violations as a newline-separated string of comma-separated values (CSV)."
}
public static let description = "Reports violations as a newline-separated string of comma-separated values (CSV)."
public static func generateReport(_ violations: [StyleViolation]) -> String {
let keys = [

View File

@ -5,10 +5,7 @@ public struct CheckstyleReporter: Reporter {
public static let identifier = "checkstyle"
public static let isRealtime = false
public var description: String {
return "Reports violations as Checkstyle XML."
}
public static let description = "Reports violations as Checkstyle XML."
public static func generateReport(_ violations: [StyleViolation]) -> String {
return [

View File

@ -10,10 +10,7 @@ public struct CodeClimateReporter: Reporter {
public static let identifier = "codeclimate"
public static let isRealtime = false
public var description: String {
return "Reports violations as a JSON array in Code Climate format."
}
public static let description = "Reports violations as a JSON array in Code Climate format."
public static func generateReport(_ violations: [StyleViolation]) -> String {
return toJSON(violations.map(dictionary(for:)))

View File

@ -4,10 +4,7 @@ public struct EmojiReporter: Reporter {
public static let identifier = "emoji"
public static let isRealtime = false
public var description: String {
return "Reports violations in the format that's both fun and easy to read."
}
public static let description = "Reports violations in the format that's both fun and easy to read."
public static func generateReport(_ violations: [StyleViolation]) -> String {
violations

View File

@ -4,10 +4,8 @@ public struct GitHubActionsLoggingReporter: Reporter {
public static let identifier = "github-actions-logging"
public static let isRealtime = true
public var description: String {
return "Reports violations in the format GitHub-hosted virtual machine for Actions can recognize as messages."
}
public static let description = "Reports violations in the format GitHub-hosted virtual " +
"machine for Actions can recognize as messages."
public static func generateReport(_ violations: [StyleViolation]) -> String {
return violations.map(generateForSingleViolation).joined(separator: "\n")

View File

@ -4,10 +4,7 @@ public struct GitLabJUnitReporter: Reporter {
public static let identifier = "gitlab"
public static let isRealtime = false
public var description: String {
return "Reports violations as JUnit XML supported by GitLab."
}
public static let description = "Reports violations as JUnit XML supported by GitLab."
public static func generateReport(_ violations: [StyleViolation]) -> String {
return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<testsuites><testsuite>" +

View File

@ -12,10 +12,7 @@ public struct HTMLReporter: Reporter {
public static let identifier = "html"
public static let isRealtime = false
public var description: String {
return "Reports violations as HTML."
}
public static let description = "Reports violations as HTML."
public static func generateReport(_ violations: [StyleViolation]) -> String {
return generateReport(violations, swiftlintVersion: Version.current.value,

View File

@ -7,10 +7,7 @@ public struct JSONReporter: Reporter {
public static let identifier = "json"
public static let isRealtime = false
public var description: String {
return "Reports violations as a JSON array."
}
public static let description: String = "Reports violations as a JSON array."
public static func generateReport(_ violations: [StyleViolation]) -> String {
return toJSON(violations.map(dictionary(for:)))

View File

@ -4,10 +4,7 @@ public struct JUnitReporter: Reporter {
public static let identifier = "junit"
public static let isRealtime = false
public var description: String {
return "Reports violations as JUnit XML."
}
public static let description = "Reports violations as JUnit XML."
public static func generateReport(_ violations: [StyleViolation]) -> String {
let warningCount = violations.filter({ $0.severity == .warning }).count

View File

@ -6,10 +6,7 @@ public struct MarkdownReporter: Reporter {
public static let identifier = "markdown"
public static let isRealtime = false
public var description: String {
return "Reports violations as markdown formated (with tables)."
}
public static let description = "Reports violations as markdown formated (with tables)."
public static func generateReport(_ violations: [StyleViolation]) -> String {
let keys = [

View File

@ -4,10 +4,7 @@ public struct RelativePathReporter: Reporter {
public static let identifier = "relative-path"
public static let isRealtime = true
public var description: String {
return "Reports violations with relative paths."
}
public static let description = "Reports violations with relative paths."
public static func generateReport(_ violations: [StyleViolation]) -> String {
return violations.map(generateForSingleViolation).joined(separator: "\n")

View File

@ -6,10 +6,7 @@ public struct SonarQubeReporter: Reporter {
public static let identifier = "sonarqube"
public static let isRealtime = false
public var description: String {
return "Reports violations in SonarQube import format."
}
public static let description = "Reports violations in SonarQube import format."
public static func generateReport(_ violations: [StyleViolation]) -> String {
return toJSON(["issues": violations.map(dictionary(for:))])

View File

@ -8,9 +8,7 @@ public struct SummaryReporter: Reporter {
public static let identifier = "summary"
public static let isRealtime = false
public var description: String {
return "Reports a summary table of all violations."
}
public static let description = "Reports a summary table of all violations."
public static func generateReport(_ violations: [StyleViolation]) -> String {
TextTable(violations: violations).renderWithExtraSeparator()

View File

@ -4,10 +4,7 @@ public struct XcodeReporter: Reporter {
public static let identifier = "xcode"
public static let isRealtime = true
public var description: String {
return "Reports violations in the format Xcode uses to display in the IDE. (default)"
}
public static let description = "Reports violations in the format Xcode uses to display in the IDE. (default)"
public static func generateReport(_ violations: [StyleViolation]) -> String {
return violations.map(generateForSingleViolation).joined(separator: "\n")

View File

@ -0,0 +1,32 @@
import ArgumentParser
import SwiftLintFramework
import SwiftyTextTable
extension SwiftLint {
struct Reporters: ParsableCommand {
static let configuration = CommandConfiguration(abstract: "Display the list of reporters and their identifiers")
func run() throws {
print(TextTable(reporters: reportersList).render())
ExitHelper.successfullyExit()
}
}
}
// MARK: - SwiftyTextTable
private extension TextTable {
init(reporters: [Reporter.Type]) {
let columns = [
TextTableColumn(header: "identifier"),
TextTableColumn(header: "description")
]
self.init(columns: columns)
for reporter in reporters {
addRow(values: [
reporter.identifier,
reporter.description
])
}
}
}

View File

@ -17,6 +17,7 @@ struct SwiftLint: AsyncParsableCommand {
Docs.self,
GenerateDocs.self,
Lint.self,
Reporters.self,
Rules.self,
Version.self
],

View File

@ -1,4 +1,4 @@
// Generated using Sourcery 2.0.1 https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 2.0.0 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
@_spi(TestHelper)
@testable import SwiftLintFramework

View File

@ -5,23 +5,7 @@ import XCTest
class ReporterTests: XCTestCase {
func testReporterFromString() {
let reporters: [Reporter.Type] = [
CheckstyleReporter.self,
CodeClimateReporter.self,
CSVReporter.self,
EmojiReporter.self,
GitHubActionsLoggingReporter.self,
GitLabJUnitReporter.self,
HTMLReporter.self,
JSONReporter.self,
JUnitReporter.self,
MarkdownReporter.self,
RelativePathReporter.self,
SonarQubeReporter.self,
SummaryReporter.self,
XcodeReporter.self
]
for reporter in reporters {
for reporter in reportersList {
XCTAssertEqual(reporter.identifier, reporterFrom(identifier: reporter.identifier).identifier)
}
}