Skip to content

Commit

Permalink
Added the changelog.combinePreReleases property to allow for combin…
Browse files Browse the repository at this point in the history
…ing pre-releases into a single section #50
  • Loading branch information
hsz committed Oct 20, 2022
1 parent b259908 commit 258db00
Show file tree
Hide file tree
Showing 10 changed files with 408 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Added the `--version=...` CLI parameter for the `getChangelog` task [#83](../../issues/83)
- Throw an exception when `initializeChangelog` task works on non-empty file [#82](../../issues/82)
- Remove empty sections from the changelog while patching [#28](../../issues/28)
- Added the `changelog.combinePreReleases` property to allow for combining pre-releases into a single section [#50](../../issues/50)

### Changed
- Upgrade minimal required Gradle version to `6.8`
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ changelog {
keepUnreleasedSection.set(true)
unreleasedTerm.set("[Unreleased]")
groups.set(listOf("Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"))
lineSeparator.set("\n")
combinePreReleases.set(true)
}
```

Expand Down Expand Up @@ -120,6 +122,8 @@ changelog {
keepUnreleasedSection = true
unreleasedTerm = "[Unreleased]"
groups = ["Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"]
lineSeparator = "\n"
combinePreReleases = true
}
```

Expand Down Expand Up @@ -148,6 +152,8 @@ Plugin can be configured with the following properties set in the `changelog {}`
| `patchEmpty` | Patches changelog even if no release note is provided. | `true` |
| `path` | Path to the changelog file. | `"${project.projectDir}/CHANGELOG.md"` |
| `unreleasedTerm` | Unreleased section text. | `"[Unreleased]"` |
| `lineSeparator` | Line separator used for generating changelog content. | `"\n"` or determined from the existing file |
| `combinePreReleases` | Combines pre-releases into the final release note when patching. | `true` |

> **Note**
>
Expand Down
34 changes: 32 additions & 2 deletions src/main/kotlin/org/jetbrains/changelog/Changelog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.ast.ASTNode
import org.intellij.markdown.ast.getTextInNode
import org.intellij.markdown.parser.MarkdownParser
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_2
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_3
import org.jetbrains.changelog.exceptions.HeaderParseException
import org.jetbrains.changelog.exceptions.MissingFileException
Expand Down Expand Up @@ -39,6 +40,8 @@ data class Changelog(

private val preTitleNodes = tree.children
.takeWhile { it.type != MarkdownElementTypes.ATX_1 }
.takeIf { tree.children.any { it.type == MarkdownElementTypes.ATX_1 } }
.orEmpty()
val preTitleValue = preTitle ?: preTitleNodes
.joinToString(lineSeparator) { it.text() }
.trim()
Expand All @@ -57,6 +60,7 @@ data class Changelog(
val introductionValue = introduction ?: introductionNodes
.joinToString(lineSeparator) { it.text() }
.reformat(lineSeparator)
.trim()

private val itemsNodes = tree.children
.dropWhile { it.type != MarkdownElementTypes.ATX_2 }
Expand All @@ -81,6 +85,7 @@ data class Changelog(
val header = value
.firstOrNull { it.type == MarkdownElementTypes.ATX_2 }?.text()
.orEmpty()
.removePrefix("$ATX_2 ")
.trim()

val nodes = value
Expand All @@ -95,6 +100,7 @@ data class Changelog(
val summary = summaryNodes
.joinToString(lineSeparator) { it.text() }
.reformat(lineSeparator)
.trim()

val items = nodes
.drop(summaryNodes.size)
Expand All @@ -112,6 +118,7 @@ data class Changelog(
it.isNotEmpty()
}
}
.toSet()

}

Expand All @@ -130,7 +137,7 @@ data class Changelog(
var version: String,
val header: String,
val summary: String,
private val items: Map<String, List<String>>,
private val items: Map<String, Set<String>> = emptyMap(),
private val isUnreleased: Boolean = false,
private val lineSeparator: String,
) {
Expand All @@ -154,7 +161,7 @@ data class Changelog(

fun toText() = sequence {
if (withHeader) {
yield(header)
yield("$ATX_2 $header")
}

if (withSummary && summary.isNotEmpty()) {
Expand Down Expand Up @@ -187,6 +194,7 @@ data class Changelog(
override fun toString() = toText()

private fun copy(
summary: String = this.summary,
withHeader: Boolean = this.withHeader,
withSummary: Boolean = this.withSummary,
withEmptySections: Boolean = this.withEmptySections,
Expand All @@ -204,6 +212,28 @@ data class Changelog(
it.withEmptySections = withEmptySections
it.filterCallback = filterCallback
}

operator fun plus(item: Item?): Item {
if (item == null) {
return copy()
}

return copy(
summary = summary.ifEmpty { item.summary },
items = items + item.items,
)
}

operator fun Map<String, Set<String>>.plus(other: Map<String, Set<String>>): Map<String, Set<String>> {

return this.mapValues { (key, value) ->
value + other[key].orEmpty()
}.toMutableMap().also { map ->
map.putAll(other.filterKeys { !this.containsKey(it) })
}
}

operator fun plus(items: List<Item>) = items.fold(this) { acc, item -> acc + item }
}

private fun ASTNode.text() = getTextInNode(content).toString()
Expand Down
4 changes: 3 additions & 1 deletion src/main/kotlin/org/jetbrains/changelog/ChangelogPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ChangelogPlugin : Plugin<Project> {
?: throw VersionNotSpecifiedException()
}
)
title.convention(ChangelogPluginConstants.DEFAULT_TITLE)
// title.convention(ChangelogPluginConstants.DEFAULT_TITLE)
lineSeparator.convention(path.map { path ->
val content = Path.of(path)
.takeIf { Files.exists(it) }
Expand All @@ -63,6 +63,7 @@ class ChangelogPlugin : Plugin<Project> {
else -> "\n"
}
})
combinePreReleases.convention(true)
}

val pathProvider = project.layout.file(extension.path.map { File(it) })
Expand Down Expand Up @@ -97,6 +98,7 @@ class ChangelogPlugin : Plugin<Project> {
unreleasedTerm.convention(extension.unreleasedTerm)
version.convention(extension.version)
lineSeparator.convention(extension.lineSeparator)
combinePreReleases.convention(extension.combinePreReleases)
}

project.tasks.register<InitializeChangelogTask>(INITIALIZE_CHANGELOG_TASK_NAME) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ abstract class ChangelogPluginExtension {
@get:Optional
abstract val lineSeparator: Property<String>

@get:Optional
abstract val combinePreReleases: Property<Boolean>

val instance
get() = Changelog(
file = File(path.get()),
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/org/jetbrains/changelog/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ internal fun compose(
yield(preTitle)
}
if (!title.isNullOrBlank()) {
yield("$ATX_1 ${title.trim()}")
yield("$ATX_1 $title")
}
if (!introduction.isNullOrBlank()) {
yield(introduction)
Expand Down
59 changes: 37 additions & 22 deletions src/main/kotlin/org/jetbrains/changelog/tasks/PatchChangelogTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.api.tasks.options.Option
import org.jetbrains.changelog.Changelog
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_1
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_2
import org.jetbrains.changelog.Version
import org.jetbrains.changelog.compose
import org.jetbrains.changelog.exceptions.MissingReleaseNoteException

Expand Down Expand Up @@ -75,6 +77,9 @@ abstract class PatchChangelogTask : DefaultTask() {
@get:Internal
abstract val lineSeparator: Property<String>

@get:Internal
abstract val combinePreReleases: Property<Boolean>

@TaskAction
fun run() {
val unreleasedTermValue = unreleasedTerm.get()
Expand All @@ -91,27 +96,39 @@ abstract class PatchChangelogTask : DefaultTask() {
)

val preTitleValue = preTitle.orNull ?: changelog.preTitleValue
val titleValue = title.orNull ?: changelog.titleValue
val titleValue = title.orNull ?: changelog.titleValue.removePrefix("$ATX_1 ")
val introductionValue = introduction.orNull ?: changelog.introductionValue
val headerValue = header.get()

val item = changelog.runCatching { get(unreleasedTermValue) }.getOrNull()
val otherItems = changelog.getAll().filterNot { it.key == unreleasedTermValue }.values
val noUnreleasedSection = item == null || item.getSections().isEmpty()
val noReleaseNote = releaseNote.isNullOrBlank()
val content = releaseNote
?: item
?.withEmptySections(false)
?.withHeader(false)
?.toText()
?: ""

val releasedItems = changelog.getAll().filterNot { it.key == unreleasedTermValue }.values
val preReleaseItems = releasedItems
.filter {
val current = Version.parse(version.get())
with(Version.parse(it.version)) {
current.major == major && current.minor == minor && current.patch == patch
}
}
.takeIf { combinePreReleases.get() }
.orEmpty()

val item = Changelog.Item(
version = version.get(),
header = header.get(),
summary = "",
isUnreleased = true,
lineSeparator = lineSeparator.get(),
) + changelog.runCatching { get(unreleasedTermValue) }.getOrNull() + preReleaseItems

val content = releaseNote ?: item
.withEmptySections(false)
.withHeader(false)
.toText()

if (patchEmpty.get() && content.isEmpty()) {
logger.info(":patchChangelog task skipped due to the missing release note in the '$unreleasedTerm'.")
throw StopActionException()
}

if (noUnreleasedSection && noReleaseNote && content.isBlank()) {
if (item.getSections().isEmpty() && content.isBlank()) {
throw MissingReleaseNoteException(
":patchChangelog task requires release note to be provided. " +
"Add '$ATX_2 $unreleasedTermValue' section header to your changelog file: " +
Expand All @@ -127,17 +144,15 @@ abstract class PatchChangelogTask : DefaultTask() {
groups = groups.get(),
lineSeparator = lineSeparator.get(),
) {
if (item != null) {
yield("$ATX_2 $headerValue")
yield("$ATX_2 ${item.header}")

if (content.isNotBlank()) {
yield(content)
} else {
yield(item.withHeader(false).toString())
}
if (content.isNotBlank()) {
yield(content)
} else {
yield(item.withHeader(false).toString())
}

otherItems.forEach {
releasedItems.forEach {
yield(it.toString())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class ChangelogPluginExtensionTest : BaseTest() {

extension.get(version).apply {
assertEquals(this@ChangelogPluginExtensionTest.version, version)
assertEquals("## [1.0.0]", header)
assertEquals("[1.0.0]", header)
assertMarkdown(
"""
First release.
Expand Down Expand Up @@ -283,7 +283,7 @@ class ChangelogPluginExtensionTest : BaseTest() {

extension.get(version).apply {
assertEquals(this@ChangelogPluginExtensionTest.version, version)
assertEquals("## [1.0.0]", header)
assertEquals("[1.0.0]", header)
withFilter {
!it.endsWith('x')
}.apply {
Expand Down Expand Up @@ -334,7 +334,7 @@ class ChangelogPluginExtensionTest : BaseTest() {
fun `returns latest change note`() {
extension.getLatest().apply {
assertEquals("[Unreleased]", version)
assertEquals("## [Unreleased]", header)
assertEquals("[Unreleased]", header)
assertMarkdown(
"""
Not yet released version.
Expand Down Expand Up @@ -441,8 +441,8 @@ class ChangelogPluginExtensionTest : BaseTest() {
assertEquals(2, keys.size)
assertEquals("[Unreleased]", keys.first())
assertEquals("1.0.0", keys.last())
assertEquals("## [Unreleased]", values.first().header)
assertEquals("## [1.0.0]", values.last().header)
assertEquals("[Unreleased]", values.first().header)
assertEquals("[1.0.0]", values.last().header)
assertMarkdown(
"""
## [Unreleased]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ class GetChangelogTaskTest : BaseTest() {
"""
## [Unreleased]
Some unreleased changes.
- bar
""".trimIndent(),
result.output.trim()
Expand Down
Loading

0 comments on commit 258db00

Please sign in to comment.