diff --git a/CHANGELOG.md b/CHANGELOG.md index 9de3d88..3223f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Next -- Nothing yet! +- Added `inline` option to `SwiftLint.lint()`. This will trigger an inline comment instead of gathering all violations in a big main comment. For more details about inline mode, see [this docs in Danger JS](https://github.com/danger/danger-js/blob/master/CHANGELOG.md#340). ## 0.2.1 diff --git a/README.md b/README.md index aa8c0b9..c4829db 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,14 @@ SwiftLint.lint() That will lint the created and modified files +#### Inline mode + +If you want the lint result shows in diff instead of comment, you can use inline_mode option. Violations that out of the diff will show in danger's fail or warn section. + +```swift +SwiftLint.lint(inline: true) +``` + # Contributing If you find a bug, please [open an issue](https://github.com/ashfurrow/danger-swiftlint/issues/new)! Or a pull request :wink: diff --git a/Sources/DangerSwiftLint/DangerSwiftLint.swift b/Sources/DangerSwiftLint/DangerSwiftLint.swift index 2baedcc..7248344 100644 --- a/Sources/DangerSwiftLint/DangerSwiftLint.swift +++ b/Sources/DangerSwiftLint/DangerSwiftLint.swift @@ -8,10 +8,10 @@ public struct SwiftLint { /// This is the main entry point for linting Swift in PRs using Danger-Swift. /// Call this function anywhere from within your Dangerfile.swift. @discardableResult - public static func lint(directory: String? = nil, configFile: String? = nil) -> [Violation] { + public static func lint(inline: Bool = false, directory: String? = nil, configFile: String? = nil) -> [Violation] { // First, for debugging purposes, print the working directory. print("Working directory: \(shellExecutor.execute("pwd"))") - return self.lint(danger: danger, shellExecutor: shellExecutor, directory: directory, configFile: configFile) + return self.lint(danger: danger, shellExecutor: shellExecutor, inline: inline, directory: directory, configFile: configFile) } } @@ -20,10 +20,13 @@ internal extension SwiftLint { static func lint( danger: DangerDSL, shellExecutor: ShellExecutor, + inline: Bool = false, directory: String? = nil, configFile: String? = nil, - markdown: (String) -> Void = markdown, - fail: (String) -> Void = fail) -> [Violation] { + markdownAction: (String) -> Void = markdown, + failAction: (String) -> Void = fail, + failInlineAction: (String, String, Int) -> Void = fail, + warnInlineAction: (String, String, Int) -> Void = warn) -> [Violation] { // Gathers modified+created files, invokes SwiftLint on each, and posts collected errors+warnings to Danger. var files = danger.git.createdFiles + danger.git.modifiedFiles @@ -38,22 +41,42 @@ internal extension SwiftLint { } let outputJSON = shellExecutor.execute("swiftlint", arguments: arguments) do { - return try decoder.decode([Violation].self, from: outputJSON.data(using: String.Encoding.utf8)!) + var violations = try decoder.decode([Violation].self, from: outputJSON.data(using: String.Encoding.utf8)!) + // Workaround for a bug that SwiftLint returns absolute path + violations = violations.map { violation in + var newViolation = violation + newViolation.update(file: file) + + return newViolation + } + + return violations } catch let error { - fail("Error deserializing SwiftLint JSON response (\(outputJSON)): \(error)") + failAction("Error deserializing SwiftLint JSON response (\(outputJSON)): \(error)") return [] } } if !violations.isEmpty { - var markdownMessage = """ - ### SwiftLint found issues - - | Severity | File | Reason | - | -------- | ---- | ------ |\n - """ - markdownMessage += violations.map { $0.toMarkdown() }.joined(separator: "\n") - markdown(markdownMessage) + if inline { + violations.forEach { violation in + switch violation.severity { + case .error: + failInlineAction(violation.reason, violation.file, violation.line) + case .warning: + warnInlineAction(violation.reason, violation.file, violation.line) + } + } + } else { + var markdownMessage = """ + ### SwiftLint found issues + + | Severity | File | Reason | + | -------- | ---- | ------ |\n + """ + markdownMessage += violations.map { $0.toMarkdown() }.joined(separator: "\n") + markdownAction(markdownMessage) + } } return violations diff --git a/Sources/DangerSwiftLint/Violation.swift b/Sources/DangerSwiftLint/Violation.swift index a8bb9ef..076d7eb 100644 --- a/Sources/DangerSwiftLint/Violation.swift +++ b/Sources/DangerSwiftLint/Violation.swift @@ -8,9 +8,10 @@ public struct Violation: Codable { let reason: String let line: Int let character: Int? - let file: String let severity: Severity let type: String + + private(set) var file: String enum CodingKeys: String, CodingKey { case ruleID = "rule_id" @@ -32,6 +33,10 @@ public struct Violation: Codable { let formattedFile = file.split(separator: "/").last! + ":\(line)" return "\(severity.rawValue) | \(formattedFile) | \(reason) |" } + + mutating func update(file: String) { + self.file = file + } } diff --git a/Tests/DangerSwiftLintTests/DangerSwiftLintTests.swift b/Tests/DangerSwiftLintTests/DangerSwiftLintTests.swift index 99f1908..c6e60fd 100644 --- a/Tests/DangerSwiftLintTests/DangerSwiftLintTests.swift +++ b/Tests/DangerSwiftLintTests/DangerSwiftLintTests.swift @@ -20,6 +20,18 @@ class DangerSwiftLintTests: XCTestCase { XCTAssertNotEqual(executor.invocations.dropFirst().count, 0) } + func testExecuteSwiftLintInInlineMode() { + mockViolationJSON() + var warns = [(String, String, Int)]() + let warnAction: (String, String, Int) -> Void = { warns.append(($0, $1, $2)) } + + _ = SwiftLint.lint(danger: danger, shellExecutor: executor, inline: true, warnInlineAction: warnAction) + + XCTAssertTrue(warns.first?.0 == "Opening braces should be preceded by a single space and on the same line as the declaration.") + XCTAssertTrue(warns.first?.1 == "SomeFile.swift") + XCTAssertTrue(warns.first?.2 == 8) + } + func testExecutesSwiftLintWithConfigWhenPassed() { let configFile = "/Path/to/config/.swiftlint.yml" @@ -58,13 +70,13 @@ class DangerSwiftLintTests: XCTestCase { func testViolations() { mockViolationJSON() - let violations = SwiftLint.lint(danger: danger, shellExecutor: executor, markdown: writeMarkdown) + let violations = SwiftLint.lint(danger: danger, shellExecutor: executor, markdownAction: writeMarkdown) XCTAssertEqual(violations.count, 2) // Two files, one (identical oops) violation returned for each. } func testMarkdownReporting() { mockViolationJSON() - _ = SwiftLint.lint(danger: danger, shellExecutor: executor, markdown: writeMarkdown) + _ = SwiftLint.lint(danger: danger, shellExecutor: executor, markdownAction: writeMarkdown) XCTAssertNotNil(markdownMessage) XCTAssertTrue(markdownMessage!.contains("SwiftLint found issues")) } @@ -92,7 +104,7 @@ class DangerSwiftLintTests: XCTestCase { "rule_id" : "opening_brace", "reason" : "Opening braces should be preceded by a single space and on the same line as the declaration.", "character" : 39, - "file" : "/Users/ash/bin/Harvey/Sources/Harvey/Harvey.swift", + "file" : "/Users/ash/bin/SomeFile.swift", "severity" : "Warning", "type" : "Opening Brace Spacing", "line" : 8