Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add commit lint implementation #4

Merged
merged 4 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Sources/DangerSwiftCommitLint/CommitChecker/BodyEmptyLine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

struct BodyEmptyLine: CommitLint, Hashable {
static var linterMessage = "Please separate commit message subject from body with newline."

private let bodyLinesOfText: [String]

init(_ commitMessage: GitCommitMessage) {
bodyLinesOfText = commitMessage.bodyLinesOfText
}

var fail: Bool {
bodyLinesOfText.isEmpty ? false : bodyLinesOfText.first?.isEmpty == false
}
}

This file was deleted.

16 changes: 0 additions & 16 deletions Sources/DangerSwiftCommitLint/CommitChecker/CommitChecker.swift

This file was deleted.

17 changes: 17 additions & 0 deletions Sources/DangerSwiftCommitLint/CommitChecker/CommitLint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Foundation

public protocol CommitLint {
static var linterMessage: String { get }

var fail: Bool { get }

init(_ commitMessage: GitCommitMessage)

static func fail(_ commitMessage: GitCommitMessage) -> Bool
}

public extension CommitLint {
static func fail(_ commitMessage: GitCommitMessage) -> Bool {
Self(commitMessage).fail
}
}
28 changes: 0 additions & 28 deletions Sources/DangerSwiftCommitLint/CommitChecker/CommitMessage.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Danger
import Foundation

/// An abstraction of Git commit message for `DangerSwiftCommitLint`.
public struct GitCommitMessage: Hashable {
/// First line of the commit message
public let subject: String
/// Rest of the commit message
public let bodyLinesOfText: [String]
// Commit SHA value
public let sha: String?

init(
subject: String,
bodyLinesOfText: [String],
sha: String
) {
self.subject = subject
self.bodyLinesOfText = bodyLinesOfText
self.sha = sha
}

/// Initialize `GitCommitMessage` with `Danger.Git.Commit.message`
/// - Parameter gitCommit: An instance of `Danger.Git.Commit`
public init(_ gitCommit: Git.Commit) {
let commitMessageLines = gitCommit.message.components(separatedBy: .newlines)
subject = commitMessageLines.first ?? ""
bodyLinesOfText = Array(commitMessageLines.dropFirst())
sha = gitCommit.sha
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

struct SubjectCapitalLetter: CommitLint, Hashable {
static let linterMessage = "Please start commit message subject with capital letter."

private let firstCharacter: Character?

init(_ commitMessage: GitCommitMessage) {
firstCharacter = commitMessage.subject.first
}

var fail: Bool {
firstCharacter?.isLowercase ?? false
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import Foundation

struct SubjectLengthCheck: CommitChecker {
struct SubjectLength: CommitLint, Hashable {
private enum GeneratedSubjectPattern {
static let git = #"^Merge branch '.+' into "#
static let gitHub = #"^Merge pull request #\d+ from "#
}

static let warningMessage = "Please limit commit message subject line to 50 characters."
static let linterMessage = "Please limit commit message subject line to 50 characters."

private let subject: String

init(message: CommitMessage) {
subject = message.subject
init(_ commitMessage: GitCommitMessage) {
subject = commitMessage.subject
}

var fail: Bool {
subject.count > 50 && isMergeCommit == false
}
}

private extension SubjectLengthCheck {
private extension SubjectLength {
var isMergeCommit: Bool {
subject =~ GeneratedSubjectPattern.git || subject =~ GeneratedSubjectPattern.gitHub
}
Expand Down
15 changes: 15 additions & 0 deletions Sources/DangerSwiftCommitLint/CommitChecker/SubjectPeriod.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

struct SubjectPeriod: CommitLint, Hashable {
static var linterMessage = "Please remove period from end of commit message subject line."

private let subject: String

init(_ commitMessage: GitCommitMessage) {
subject = commitMessage.subject
}

var fail: Bool {
subject.last == "."
}
}

This file was deleted.

15 changes: 15 additions & 0 deletions Sources/DangerSwiftCommitLint/CommitChecker/SubjectWord.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

struct SubjectWord: CommitLint, Hashable {
static var linterMessage = "Please use more than one word in commit message."

private let subject: String

init(_ commitMessage: GitCommitMessage) {
subject = commitMessage.subject
}

var fail: Bool {
subject.split(separator: " ").count < 2
}
}

This file was deleted.

109 changes: 109 additions & 0 deletions Sources/DangerSwiftCommitLint/Configuration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import Foundation

public extension DangerSwiftCommitLint {
/// All commit checkers provided by `DangerSwiftCommitLint`
enum CommitCheckerType: CaseIterable, Hashable {
/// Commit subject and body are separated by an empty line (`CommitChecker/BodyEmptyLineCheck`)
case bodyEmptyLine
/// Commit subject begins with a capital letter (`CommitChecker/SubjectCapCheck`)
case subjectCapitalLetter
/// Commit subject is no longer than 50 characters (`CommitChecker/SubjectLengthCheck`)
case subjectLength
/// Commit subject does not end in a period (`CommitChecker/SubjectPeriodCheck`)
case subjectPeriod
/// Commit subject is more than one word (`CommitChecker/SubjectWordCheck`)
case subjectWord
}

/// Checker selection
enum CommitCheckerSelection {
/// Select all checkers
case all
/// Select a set of checkers
case selected(Set<CommitCheckerType>)
}

struct Configuration {
let limit: Int

private let disabled: CommitCheckerSelection
private let warn: CommitCheckerSelection
private let fail: CommitCheckerSelection
private let customCheckers: [CommitLint.Type]

/// Initialize the configuration.
/// - Parameters:
/// - disabled: The selected checks to skip.
/// - warn: The selected checks to warn on.
/// - fail: The selected checks to fail on.
/// - limit: The number of commits to check.
/// - customCheckers: An array of custom checkers.
public init(
disabled: CommitCheckerSelection = .selected([]),
warn: CommitCheckerSelection = .selected([]),
fail: CommitCheckerSelection = .all,
limit: Int = 0,
customCheckers: [CommitLint.Type] = []
) {
self.disabled = disabled
self.warn = warn
self.fail = fail
self.limit = limit
self.customCheckers = customCheckers
}
}
}

extension DangerSwiftCommitLint.Configuration {
static var defaultCheckers: [CommitLint.Type] {
DangerSwiftCommitLint.CommitCheckerType.allCases.map(\.type)
}

var allCheckers: [CommitLint.Type] {
Self.defaultCheckers + customCheckers
}

var disabledCheckers: [CommitLint.Type] {
switch disabled {
case .all:
return allCheckers
case let .selected(disabled):
return disabled.map(\.type)
}
}

var enabledCheckers: [CommitLint.Type] {
allCheckers.filter { checker in
disabledCheckers.contains { $0 == checker } == false
}
}

var warningCheckers: [CommitLint.Type] {
switch warn {
case .all:
return allCheckers
case let .selected(warningCheckers):
return enabledCheckers.filter { type in
warningCheckers.map(\.type).contains { $0 == type }
}
}
}

var failingCheckers: [CommitLint.Type] {
enabledCheckers.filter { type in
warningCheckers.contains { $0 == type } == false
}
}
}

private extension DangerSwiftCommitLint.CommitCheckerType {
var type: CommitLint.Type {
switch self {
case .bodyEmptyLine: return BodyEmptyLine.self
case .subjectCapitalLetter: return SubjectCapitalLetter.self
case .subjectLength: return SubjectCapitalLetter.self
case .subjectPeriod: return SubjectPeriod.self
case .subjectWord: return SubjectWord.self
}
}
}
Loading