Skip to content

Commit

Permalink
feat: Change log DSL
Browse files Browse the repository at this point in the history
  • Loading branch information
jmongard committed Sep 23, 2023
1 parent 1372b84 commit cdd531a
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 28 deletions.
59 changes: 46 additions & 13 deletions src/main/kotlin/git/semver/plugin/changelog/ChangeLogFormatter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ class ChangeLogFormatter(private val settings: SemverSettings, private val forma
private val changeLogRegex = format.changeLogPattern.toRegex(SemverSettings.REGEX_OPTIONS)

internal fun formatLog(changeLog: List<Commit>): String {
val infos = commitInfos(changeLog)

return formatLog(infos)
}

private fun formatLog(infos: List<CommitInfo>): String {
val groupedByHeading = TreeMap<String, MutableSet<String>>()
changeLog.sortedBy { it.text }.groupBy { it.text }.forEach { addCommit(it, groupedByHeading) }

infos.sortedBy { it.text }.forEach { addCommit(it, groupedByHeading) }

val builder = StringBuilder()
addStaticText(builder, format.header)

groupedByHeading.forEach { (heading, items) ->
addStaticText(builder, format.groupStart)
builder.appendLine().appendLine(heading)
Expand All @@ -37,46 +42,65 @@ class ChangeLogFormatter(private val settings: SemverSettings, private val forma
return builder.trim().toString()
}

fun commitInfos(changeLog: List<Commit>): List<CommitInfo> {
return changeLog.groupBy { it.text }.map { commitInfo(it.key, it.value) }
}

private fun commitInfo(commitText: String, commits: List<Commit>): CommitInfo {
val isBreaking = settings.majorRegex.containsMatchIn(commitText)
val match = changeLogRegex.find(commitText)
return if (match != null) {
CommitInfo(
commits,
commitText,
isBreaking,
match.groupValue(TYPE),
match.groupValue(SCOPE),
match.groupValue(MESSAGE)
)
} else {
CommitInfo(commits, commitText, isBreaking)
}
}

private fun addCommit(
commit: Map.Entry<String, List<Commit>>,
commit: CommitInfo,
resultMap: MutableMap<String, MutableSet<String>>
) {
fun addChangeLogText(category: String?, message: String, scope: String? = null) {
if (!category.isNullOrEmpty()) {
resultMap.computeIfAbsent(category) { mutableSetOf() }.add(formatLine(scope, message, commit.value).trim())
resultMap.computeIfAbsent(category) { mutableSetOf() }.add(formatLine(scope, message, commit).trim())
}
}

val text = commit.key
if (format.breakingChangeHeader != null && settings.majorRegex.containsMatchIn(text)) {
val text = commit.text
if (format.breakingChangeHeader.isNotEmpty() && commit.isBreaking) {
addChangeLogText(format.breakingChangeHeader, text)
return
}

val match = changeLogRegex.find(text)
if (match == null) {
if (commit.type == null) {
addChangeLogText(format.missingTypeHeader, text)
return
}

val scope = match.groupValue(SCOPE)
val scope = commit.scope
val scopeHeading = scope?.let { format.headerTexts[it] }
if (scopeHeading != null) {
addChangeLogText(scopeHeading, text)
return
}

val typeHeading = format.headerTexts[match.groupValue(TYPE)]
val typeHeading = format.headerTexts[commit.type]
if (typeHeading != null) {
val message = match.groupValue(MESSAGE)!!
addChangeLogText(typeHeading, message, scope)
addChangeLogText(typeHeading, commit.message!!, scope)
return
}

addChangeLogText(format.otherChangeHeader, text)
}

private fun formatLine(scope: String?, message: String, commits: List<Commit>) = commits
private fun formatLine(scope: String?, message: String, commitInfo: CommitInfo) = commitInfo.commits
.map { it.sha.take(format.changeShaLength) }
.filter { it.isNotEmpty() }
.joinToString(" ", "", " ") +
Expand All @@ -87,4 +111,13 @@ class ChangeLogFormatter(private val settings: SemverSettings, private val forma
text?.let(builder::appendLine)

private fun MatchResult.groupValue(groupId: String) = this.groups[groupId]?.value

class CommitInfo(
val commits: List<Commit>,
val text: String,
val isBreaking: Boolean,
val type: String? = null,
val scope: String? = null,
val message: String? = null
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package git.semver.plugin.changelog

data class ChangeLogSettings(
var header: String? = null,
var breakingChangeHeader: String? = null,
var otherChangeHeader: String? = null,
var missingTypeHeader: String? = null,
var breakingChangeHeader: String = "",
var otherChangeHeader: String = "",
var missingTypeHeader: String = "",
var missingScopeHeader: String = "",
var headerTexts: MutableMap<String, String> = mutableMapOf(),
var changePrefix: String = "",
var changePostfix: String = "",
Expand Down Expand Up @@ -36,7 +37,7 @@ data class ChangeLogSettings(
"refactor" to "### Refactorings \uD83D\uDE9C",
"release" to ""
),
"- ")
changePrefix = "- ")
val simpleChangeLog
get() = ChangeLogSettings(
breakingChangeHeader = "## Breaking Changes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class ChangeLogFormatterTest {
fun format_log_empty() {
val settings = SemverSettings()

val actual = ChangeLogFormatter(settings, ChangeLogSettings.defaultChangeLog).formatLog(listOf())
val format = ChangeLogSettings.defaultChangeLog
val actual = ChangeLogFormatter(settings, format).formatLog(listOf())

assertThat(actual).startsWith("## What's Changed")
}
Expand All @@ -29,14 +30,14 @@ class ChangeLogFormatterTest {
.startsWith("## What's Changed")
.containsOnlyOnce("Bugfix 1")
.contains("### Breaking Changes")
.contains("- 0050000 fix(#5)!: A breaking change")
.contains("- 0050000 fix(changelog)!: A breaking change")
.contains("### Bug Fixes")
.contains("- 0060000 build(deps): A build change")
.contains("- 0090000 A CI change")
.contains("### Tests")
.contains("- 0080000 Added some tests")
.contains("### New Features")
.contains("- 0040000 #2: A feature")
.contains("- 0040000 semver: A feature")
.contains("- 0100000 xyz: Some other change")
.contains("- 0110000 An uncategorized change")
.doesNotContain("1.2.3")
Expand All @@ -54,12 +55,12 @@ class ChangeLogFormatterTest {
assertThat(actual)
.containsOnlyOnce("Bugfix 1")
.contains("## Breaking Changes")
.contains("- fix(#5)!: A breaking change")
.contains("- fix(changelog)!: A breaking change")
.contains("## Bug Fixes")
.doesNotContain("- build(deps): A build change")
.doesNotContain("- A CI change")
.contains("## New Features")
.contains("- #2: A feature")
.contains("- semver: A feature")
.doesNotContain("- xyz: Some other change")
.doesNotContain("- An uncategorized change")
.doesNotContain("1.2.3")
Expand All @@ -82,9 +83,9 @@ class ChangeLogFormatterTest {
.startsWith("## What's Changed")
.containsOnlyOnce("Bugfix 1")
.contains("### Breaking Changes")
.contains("- fix(#5)!: A breaking change")
.contains("- fix(changelog)!: A breaking change")
.doesNotContain("1.2.3")
.contains(" more text")
.contains(" Body text")
println(actual)
}

Expand All @@ -100,11 +101,11 @@ class ChangeLogFormatterTest {

private fun createChangeLog(): List<Commit> {
val changeLog = mapOf(
"0010000000" to "fix(#1): Bugfix 1",
"0020000000" to "fix(#1): Bugfix 1",
"0010000000" to "fix(changelog): Bugfix 1 #1",
"0020000000" to "fix(changelog): Bugfix 1 #1",
"0030000000" to "fix(deps): Bugfix broken deps",
"0040000000" to "feat(#2): A feature",
"0050000000" to "fix(#5)!: A breaking change\nmore text",
"0040000000" to "feat(semver): A feature",
"0050000000" to "fix(changelog)!: A breaking change\n\nBody text",
"0060000000" to "build(deps): A build change",
"0070000000" to "release: 1.2.3-alpha",
"0080000000" to "test: Added some tests",
Expand Down
Loading

0 comments on commit cdd531a

Please sign in to comment.