Skip to content

Commit

Permalink
Make release notes more organized
Browse files Browse the repository at this point in the history
  • Loading branch information
Piotr Adamczyk committed Aug 19, 2020
1 parent 7d214bf commit 7974e9f
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
package flank.scripts.ci.releasenotes

import flank.scripts.utils.markdownH2
import java.io.File

fun File.appendReleaseNotes(messages: List<String>, releaseTag: String) {
fun File.appendReleaseNotes(releaseNotesWithType: ReleaseNotesWithType, releaseTag: String) {
appendToReleaseNotes(
messages = messages,
releaseNotesWithType = releaseNotesWithType,
releaseTag = releaseTag
)
}

private fun File.appendToReleaseNotes(messages: List<String>, releaseTag: String) {
val currentFileLines = readLines()
val newLines = mutableListOf<String>().apply {
add(releaseTag.markdownH2())
addAll(messages)
add("")
}

writeText((newLines + currentFileLines).joinToString(System.lineSeparator()).withNewLineAtTheEnd())
private fun File.appendToReleaseNotes(releaseNotesWithType: ReleaseNotesWithType, releaseTag: String) {
writeText(releaseNotesWithType.asString(releaseTag).withNewLineAtTheEnd() +
readLines().joinToString(System.lineSeparator()).withNewLineAtTheEnd())
}

private fun String.withNewLineAtTheEnd() = plus(System.lineSeparator())
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package flank.scripts.ci.releasenotes

import flank.scripts.utils.markdownBold

fun String.mapPrTitle() = when {
startsWith("feat") -> replaceFirst("feat", "New feature".markdownBold())
startsWith("fix") -> replaceFirst("fix", "Fix".markdownBold())
startsWith("docs") -> replaceFirst("docs", "Documentation".markdownBold())
startsWith("refactor") -> replaceFirst("refactor", "Refactor".markdownBold())
startsWith("ci") -> replaceFirst("ci", "CI changes".markdownBold())
startsWith("test") -> replaceFirst("test", "Tests update".markdownBold())
startsWith("perf") -> replaceFirst("perf", "Performance upgrade".markdownBold())
fun String.mapPrTitleWithType() = when {
startsWith("feat") -> "Features" to skipConventionalCommitPrefix().capitalize()
startsWith("fix") -> "Bug Fixes" to skipConventionalCommitPrefix().capitalize()
startsWith("docs") -> "Documentation" to skipConventionalCommitPrefix().capitalize()
startsWith("refactor") -> "Refactor" to skipConventionalCommitPrefix().capitalize()
startsWith("ci") -> "CI Changes" to skipConventionalCommitPrefix().capitalize()
startsWith("test") -> "Tests update" to skipConventionalCommitPrefix().capitalize()
startsWith("perf") -> "Performance upgrade" to skipConventionalCommitPrefix().capitalize()
else -> null // we do not accept other prefix to have update in release notes
}

private fun String.skipConventionalCommitPrefix() = substring(indexOf(':') + 2)
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fun generateReleaseNotes(githubToken: String) = runBlocking {
fun generateReleaseNotes(
latestReleaseTag: String,
githubToken: String
) = getCommitsSha(latestReleaseTag).getReleaseNotes(githubToken)
) = getCommitsSha(latestReleaseTag).getNewReleaseNotes(githubToken)

internal fun getCommitsSha(fromTag: String): List<String> {
val outputFile = File.createTempFile("sha", ".log")
Expand All @@ -32,18 +32,27 @@ internal fun getCommitsSha(fromTag: String): List<String> {
return outputFile.readLines()
}

private fun List<String>.getReleaseNotes(githubToken: String) = runBlocking {
private fun List<String>.getNewReleaseNotes(githubToken: String) = runBlocking {
map { sha -> async { getPrDetailsByCommit(sha, githubToken) } }
.awaitAll()
.filterIsInstance<Result.Success<List<GithubPullRequest>>>()
.map { it.value }
.filter { it.isNotEmpty() }
.mapNotNull { it.first().toReleaseNoteMessage() }
.mapNotNull { it.value.firstOrNull()?.toReleaseNoteMessage() }
.fold(mutableMapOf<String, MutableList<String>>()) { grouped, pr ->
grouped.apply {
val (type, message) = pr
appendToType(type, message)
}
}
}

private fun MutableMap<String, MutableList<String>>.appendToType(
type: String,
message: String
) = apply { (getOrPut(type) { mutableListOf() }).add(message) }

private fun GithubPullRequest.toReleaseNoteMessage() =
title.mapPrTitle()?.let { title ->
"- ${markdownLink(number.toString(), htmlUrl)} $title ${assignees.format()}"
title.mapPrTitleWithType()?.let { (type, title) ->
type to "- ${markdownLink("#$number", htmlUrl)} $title ${assignees.format()}"
}

private fun List<GithubUser>.format() = "(${joinToString { (login, url) -> markdownLink(login, url) }})"
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import com.github.kittinunf.result.map
import com.github.kittinunf.result.success
import flank.scripts.ci.nexttag.generateNextReleaseTag
import flank.scripts.github.getLatestReleaseTag
import java.io.File
import kotlinx.coroutines.runBlocking
import java.io.File

class GenerateReleaseNotesCommand :
CliktCommand("Command to append item to release notes", name = "generateReleaseNotes") {
Expand All @@ -18,12 +19,14 @@ class GenerateReleaseNotesCommand :

override fun run() {
runBlocking {
getLatestReleaseTag(token).success {
File(releaseNotesFile).appendReleaseNotes(
messages = generateReleaseNotes(it.tag, token),
releaseTag = generateNextReleaseTag(it.tag)
)
}
getLatestReleaseTag(token)
.map { it.tag }
.success { previousTag ->
File(releaseNotesFile).appendReleaseNotes(
releaseNotesWithType = generateReleaseNotes(previousTag, token),
releaseTag = generateNextReleaseTag(previousTag)
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package flank.scripts.ci.releasenotes

import flank.scripts.utils.markdownH2
import flank.scripts.utils.markdownH3
import java.lang.StringBuilder

typealias ReleaseNotesWithType = Map<String, List<String>>

fun ReleaseNotesWithType.asString(headerTag: String) =
StringBuilder(headerTag.markdownH2())
.appendln()
.apply {
this@asString.forEach { (type, messages) ->
appendln(type.markdownH3())
messages.forEach { appendln(it) }
}
}
.toString()
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package flank.scripts.release.hub

import flank.scripts.ci.releasenotes.ReleaseNotesWithType
import flank.scripts.ci.releasenotes.generateReleaseNotes
import flank.scripts.utils.markdownH2
import flank.scripts.utils.runCommand
Expand Down Expand Up @@ -38,7 +39,7 @@ private fun hubStableReleaseCommand(path: String, gitTag: String, token: String)
gitTag
)

private fun List<String>.asReleaseBody(tag: String) =
private fun ReleaseNotesWithType.asReleaseBody(tag: String) =
StringBuilder(tag.markdownH2())
.appendln()
.apply { this@asReleaseBody.forEach { appendln(it) } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ fun String.markdownBold() = "**$this**"
fun markdownLink(description: String, url: String) = "[$description]($url)"

fun String.markdownH2() = "## $this"

fun String.markdownH3() = "### $this"
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package flank.scripts.ci.releasenotes

import com.google.common.truth.Truth.assertThat
import flank.scripts.utils.markdownH2
import flank.scripts.utils.markdownH3
import java.io.File
import org.junit.Test

Expand All @@ -13,21 +14,39 @@ class AppendReleaseNotesTest {
val testFile = File.createTempFile("empty", ".md")
val tag = "v20.08.0"
val expectedHeader = tag.markdownH2()
val messages = listOf(
"- [#11](https://github.com/Flank/flank/pull/11) **New feature**: Tests ([test_of](https://github.com/test_of))",
"- [#12](https://github.com/Flank/flank/pull/12) **New feature**: Tests2 ([test_of](https://github.com/test_of))",
"- [#13](https://github.com/Flank/flank/pull/13) **New feature**: Tests3 ([test_of](https://github.com/test_of))"
val messages = mapOf(
"Features" to listOf(
"- [#1](https://github.com/Flank/flank/pull/1) Tests1 ([test_of](https://github.com/test_of))",
"- [#2](https://github.com/Flank/flank/pull/2) Tests2 ([test_of](https://github.com/test_of))",
"- [#3](https://github.com/Flank/flank/pull/3) Tests3 ([test_of](https://github.com/test_of))"
),
"Bug fixes" to listOf(
"- [#11](https://github.com/Flank/flank/pull/11) Tests11 ([test_of](https://github.com/test_of))",
"- [#12](https://github.com/Flank/flank/pull/12) Tests12 ([test_of](https://github.com/test_of))",
"- [#13](https://github.com/Flank/flank/pull/13) Tests13 ([test_of](https://github.com/test_of))"
),
"Documentation" to listOf(
"- [#21](https://github.com/Flank/flank/pull/21) Tests21 ([test_of](https://github.com/test_of))",
"- [#22](https://github.com/Flank/flank/pull/22) Tests22 ([test_of](https://github.com/test_of))",
"- [#23](https://github.com/Flank/flank/pull/23) Tests33 ([test_of](https://github.com/test_of))"
)
)
val expectedLinesCount = testFile.readLines().size + messages.size + 2 // header + newline
val expectedLinesCount = testFile.readLines().size +
12 + // values to append
3 // header + newline + end newline

// when
testFile.appendReleaseNotes(messages, tag)

// verify
val lines = testFile.readLines()
assertThat(lines.first()).isEqualTo(expectedHeader)
messages.forEachIndexed { index, text ->
assertThat(lines[index + 1]).isEqualTo(text)
var currentLine = 1
messages.keys.forEach { type ->
assertThat(lines[currentLine++]).isEqualTo(type.markdownH3())
messages[type]?.forEach { line ->
assertThat(lines[currentLine++]).isEqualTo(line)
}
}
assertThat(lines.last()).isEmpty()
assertThat(lines.size).isEqualTo(expectedLinesCount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,40 @@ import org.junit.runner.RunWith
import org.junit.runners.Parameterized

@RunWith(Parameterized::class)
class ConventionalCommitFormatterTest(private val mapFrom: String, private val expected: String?) {
class ConventionalCommitFormatterTest(
private val mapFrom: String,
private val expectedType: String?,
private val expectedTitle: String?
) {

companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0} to {1}")
fun data() = listOf(
arrayOf("feat: Sample", "New feature"),
arrayOf("fix: Bug", "Fix"),
arrayOf("docs: New", "Documentation"),
arrayOf("refactor: Eveerything", "Refactor"),
arrayOf("ci(cd): actions", "CI changes"),
arrayOf("test(ios): update", "Tests update"),
arrayOf("perf!: 10%", "Performance upgrade"),
arrayOf("style: dd", null),
arrayOf("build: passed", null),
arrayOf("chore: release", null),
arrayOf("nope: nbd", null)
arrayOf("feat: Sample", "Features", "Sample"),
arrayOf("fix: Bug", "Bug Fixes", "Bug"),
arrayOf("docs: New", "Documentation", "New"),
arrayOf("refactor: Eveerything", "Refactor", "Eveerything"),
arrayOf("ci(cd): actions", "CI Changes", "Actions"),
arrayOf("test(ios): update", "Tests update", "Update"),
arrayOf("perf!: 10%", "Performance upgrade", "10%"),
arrayOf("style: dd", null, null),
arrayOf("build: passed", null, null),
arrayOf("chore: release", null, null),
arrayOf("nope: nbd", null, null)
)
}

@Test
fun `Should properly map`() {
// given
val expected = expected?.let { "**$it**" }

// when
val actual = mapFrom.mapPrTitle()
val actual = mapFrom.mapPrTitleWithType()

// then
if (expected != null) {
assertThat(actual).startsWith(expected)
if (expectedType != null) {
val (type, title) = actual!!
assertThat(type).isEqualTo(expectedType)
assertThat(title).startsWith(expectedTitle)
} else {
assertThat(actual).isNull()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package flank.scripts.ci.releasenotes

import org.junit.Assert.assertEquals
import org.junit.Test

class ReleaseNotesWithTypeTest {

@Test
fun `Should properly format release notes with type`() {
// given
val testTag = "TAG"
val expected = """
## TAG
### Features
- [#1](https://github.com/Flank/flank/pull/1) Tests1 ([test_of](https://github.com/test_of))
- [#2](https://github.com/Flank/flank/pull/2) Tests2 ([test_of](https://github.com/test_of))
- [#3](https://github.com/Flank/flank/pull/3) Tests3 ([test_of](https://github.com/test_of))
### Bug fixes
- [#11](https://github.com/Flank/flank/pull/11) Tests11 ([test_of](https://github.com/test_of))
- [#12](https://github.com/Flank/flank/pull/12) Tests12 ([test_of](https://github.com/test_of))
- [#13](https://github.com/Flank/flank/pull/13) Tests13 ([test_of](https://github.com/test_of))
### Documentation
- [#21](https://github.com/Flank/flank/pull/21) Tests21 ([test_of](https://github.com/test_of))
- [#22](https://github.com/Flank/flank/pull/22) Tests22 ([test_of](https://github.com/test_of))
- [#23](https://github.com/Flank/flank/pull/23) Tests33 ([test_of](https://github.com/test_of))
""".trimIndent()
val releaseNotesWithType = mapOf(
"Features" to listOf(
"- [#1](https://github.com/Flank/flank/pull/1) Tests1 ([test_of](https://github.com/test_of))",
"- [#2](https://github.com/Flank/flank/pull/2) Tests2 ([test_of](https://github.com/test_of))",
"- [#3](https://github.com/Flank/flank/pull/3) Tests3 ([test_of](https://github.com/test_of))"
),
"Bug fixes" to listOf(
"- [#11](https://github.com/Flank/flank/pull/11) Tests11 ([test_of](https://github.com/test_of))",
"- [#12](https://github.com/Flank/flank/pull/12) Tests12 ([test_of](https://github.com/test_of))",
"- [#13](https://github.com/Flank/flank/pull/13) Tests13 ([test_of](https://github.com/test_of))"
),
"Documentation" to listOf(
"- [#21](https://github.com/Flank/flank/pull/21) Tests21 ([test_of](https://github.com/test_of))",
"- [#22](https://github.com/Flank/flank/pull/22) Tests22 ([test_of](https://github.com/test_of))",
"- [#23](https://github.com/Flank/flank/pull/23) Tests33 ([test_of](https://github.com/test_of))"
)
)

// when
val actual = releaseNotesWithType.asString(testTag)

// then
assertEquals(expected, actual)
}
}

0 comments on commit 7974e9f

Please sign in to comment.