feat: rewrite commit parsing to support revert/reapply and merge commits
ci/woodpecker/push/gradle Pipeline failed Details
ci/woodpecker/push/pages Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2024-03-25 14:04:11 +01:00
parent 6d822fc915
commit 44a820e641
Signed by: Johannes
GPG Key ID: E76429612C2929F4
3 changed files with 43 additions and 17 deletions

View File

@ -4,25 +4,48 @@ enum class CommitType {
FIX, FEAT, BREAKING;
companion object {
private val pattern: Regex = Regex("([a-zA-Z ]+)(?:\\([a-zA-Z0-9 -]+\\))?(!)?: .+", RegexOption.DOT_MATCHES_ALL)
private val conventionalPattern = Regex("^(\\w+)(\\(([\\w\\-.,\\s:]+)\\)?)?(!?)[\\s?]*:(.+)")
private val footerPattern = Regex("^(BREAKING[ -]CHANGE|[^ ]+)(((: )|( #))(.+))")
fun from(commitMessage: String, warn: (String) -> Unit): CommitType {
val match = pattern.matchEntire(commitMessage)
if (match != null) {
val m = match.groupValues
if (m[2] == "!") return BREAKING
return when (m[1].lowercase()) {
"fix", "build", "chore", "ci", "docs", "style", "refactor", "perf", "test" -> FIX
"feat" -> FEAT
"breaking change", "breaking" -> BREAKING
else -> {
warn("Unrecognized commit type: ${m[1]}, guessing FEAT")
FEAT
}
val lines = commitMessage.trim().split("\\r?\\n")
val headerMatch = conventionalPattern.matchEntire(extractHeader(lines[0]))
if (headerMatch == null) {
warn("Could not parse commit, guessing type is FEAT: $commitMessage")
return FEAT
}
if (headerMatch.groupValues[3] == "!") return BREAKING
for (s in lines.drop(1).filterNot { it.isBlank() }) {
val footerMatch = footerPattern.matchEntire(s)
if (footerMatch != null) {
val type = footerMatch.groupValues[1].lowercase()
if (type == "breaking change" || type == "breaking-change") return BREAKING
}
}
warn("Could not parse commit, guessing type is FEAT: $commitMessage")
return FEAT
return when (headerMatch.groupValues[1].lowercase()) {
"fix", "build", "chore", "ci", "docs", "style", "refactor", "perf", "test" -> FIX
"feat" -> FEAT
"breaking change", "breaking" -> BREAKING
else -> {
warn("Unrecognized commit type: ${headerMatch.groupValues[1]}, guessing FEAT")
FEAT
}
}
}
private val revertPattern = Regex("^Revert \"(.+)\"", RegexOption.IGNORE_CASE)
private val reapplyPattern = Regex("^Reapply \"(.+)\"", RegexOption.IGNORE_CASE)
private tailrec fun extractHeader(headerLine: String): String {
if (headerLine.startsWith("Revert ")) {
val revertMatch = revertPattern.matchEntire(headerLine)
if (revertMatch != null) return extractHeader(revertMatch.groupValues[1])
}
if (headerLine.startsWith("Reapply ")) {
val reapplyMatch = reapplyPattern.matchEntire(headerLine)
if (reapplyMatch != null) return extractHeader(reapplyMatch.groupValues[1])
}
return headerLine
}
}
}

View File

@ -76,7 +76,7 @@ fun RevCommit.resolve(repo: Repository): Commit = Commit(
),
fullMessage,
shortMessage,
parents.map { ObjectId.toString(it) }
parents?.map { ObjectId.toString(it) } ?: emptyList()
)
private val String?.nullable get() = if (this == null || this == "") null else this
@ -86,4 +86,6 @@ data class Tag(val id: ObjectId, val original: RevTag?, val fullName: String, va
val peeledId: ObjectId get() = commit.original.id
}
data class Commit(val original: RevCommit, val id: ObjectId, val abbreviatedId: String?, val committer: Person, val author: Person, val dateTime: ZonedDateTime, val fullMessage: String, val shortMessage: String, val parentIds: List<String>)
data class Person(val original: PersonIdent, val name: String, val emailAddress: String)
data class Person(val original: PersonIdent, val name: String, val emailAddress: String)
private val Commit.isMerge get() = parentIds.size > 1

View File

@ -30,6 +30,7 @@ if (File(projectDir, ".git").exists()) {
val log = git.log(tags[0].peeledId, git.repository.resolve("HEAD")).reversed()
changelog += log.joinToString("\n") { "- ${it.shortMessage}" }
val type = log.stream()
.filter { !it.isMerge }
.map { it.fullMessage }
.map { msg -> CommitType.from(msg) { logger.warn(it) } }
.max { o1, o2 -> o1.compareTo(o2) }