Skip to content

Commit 08d4e3b

Browse files
Release notes generator: RC support and ignore prereleases to generate changelog since previous release (#503)
## What is the goal of this PR? We update the release notes generator tool to be able to handle `-rc1` in a version tag, and to generate the complete change log during a full release since previous full release. We also only emit a PR description once per PR. Github lists as pulls for a given commit all pull requests involving the commit, which includes e.g. merges from development into master. We now restrict each PR description to only be emitted once, and fallback to the commit message if all PRs have been filtered out.
1 parent e0e7b9d commit 08d4e3b

File tree

3 files changed

+76
-55
lines changed

3 files changed

+76
-55
lines changed

tool/release/notes/Commit.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,18 @@ private fun getPrecedingVersion(org: String, repo: String, version: Version, git
5454
tags.addAll(body.asArray().map { release -> Version.parse(release.asObject().get("tag_name").asString()) })
5555
tags.sort()
5656
val currentIdx = tags.indexOf(version)
57-
val preceding =
58-
if (currentIdx >= 1) tags[currentIdx - 1]
59-
else if (currentIdx == 0) null
60-
else throw IllegalStateException("Version '$version' not found: currentIdx = '$currentIdx'")
57+
val preceding = when {
58+
currentIdx < 0 -> throw IllegalStateException("Version '$version' not found: currentIdx = '$currentIdx'")
59+
currentIdx == 0 -> null
60+
version.isPrerelease() -> tags[currentIdx - 1]
61+
else -> {
62+
var previousRelease = currentIdx - 1
63+
while (previousRelease >= 0 && tags[previousRelease].isPrerelease()) previousRelease--
64+
65+
if (previousRelease < 0) null
66+
else tags[previousRelease]
67+
}
68+
}
6169
return preceding
6270
}
6371

tool/release/notes/Note.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,16 @@ ${others.map(Note::toMarkdown).joinToString("\n")}
124124
}
125125

126126
fun collectNotes(org: String, repo: String, commits: List<String>, githubToken: String): List<Note> {
127+
val seenPRs = HashSet<Int>()
127128
return commits.flatMap { commit ->
128129
val pullsRes = httpGet("$github/repos/$org/$repo/commits/$commit/pulls", githubToken)
129130
val pullsJSON = Json.parse(pullsRes.parseAsString())
130-
val prs = pullsJSON.asArray()
131-
if (prs.size() > 0) {
131+
val prs = pullsJSON.asArray().filterNot { pr -> seenPRs.contains(pr.asObject().get("number").asInt()) }
132+
if (prs.size > 0) {
132133
val notes = prs.map { pr ->
133134
val prNumber = pr.asObject().get("number").asInt()
134135
println("collecting PR #$prNumber from commit '$commit'...")
136+
seenPRs.add(prNumber)
135137
Note.fromGithubPR(pr.asObject())
136138
}
137139
notes

tool/release/notes/Version.kt

Lines changed: 60 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -21,65 +21,76 @@
2121

2222
package com.vaticle.dependencies.tool.release.notes
2323

24-
data class Version(val major: Int, val minor: Int, val patch: Int, val alpha: Int?): Comparable<Version> {
24+
data class Version(val major: Int, val minor: Int, val patch: Int, val rc: Int?, val alpha: Int?): Comparable<Version> {
2525
companion object {
2626
fun parse(version: String): Version {
27-
val version1 = version.split("-")
28-
require(version1.isNotEmpty() && version1.size <= 3) {
29-
"version '$version1' does not follow the form 'x.y.z', 'x.y.z-alpha', or 'x.y.z-alpha-n'"
30-
}
31-
val version2 = version1[0].split(".")
32-
if (version1.size == 1) {
33-
require(version2.size == 3) { "version must be of the form x.y.z" }
34-
return Version(
35-
major = version2[0].toInt(),
36-
minor = version2[1].toInt(),
37-
patch = version2[2].toInt(),
38-
alpha = null
39-
)
40-
} else if (version1.size == 2) {
41-
return Version(
42-
major = version2[0].toInt(),
43-
minor = version2[1].toInt(),
44-
patch = version2[2].toInt(),
45-
alpha = 1
46-
)
47-
} else {
48-
return Version(
49-
major = version2[0].toInt(),
50-
minor = version2[1].toInt(),
51-
patch = version2[2].toInt(),
52-
alpha = version1[2].toInt()
53-
)
27+
val components = version.split("-")
28+
val (major, minor, patch) = components[0].split(".").map(String::toInt)
29+
return when (components.size) {
30+
1 -> Version(major = major, minor = minor, patch = patch, rc = null, alpha = null)
31+
2 -> when {
32+
components[1] == "alpha" -> Version(major = major, minor = minor, patch = patch, rc = null, alpha = 1)
33+
components[1].startsWith("rc") -> Version(
34+
major = major,
35+
minor = minor,
36+
patch = patch,
37+
rc = components[1].removePrefix("rc").toInt(),
38+
alpha = null
39+
)
40+
else -> {
41+
throw IllegalArgumentException(
42+
"version '$version' does not follow the form 'x.y.z', 'x.y.z-rcN', 'x.y.z-alpha', or 'x.y.z-alpha-N'"
43+
)
44+
}
45+
}
46+
3 -> {
47+
require(components[1] == "alpha") {
48+
"version '$version' does not follow the form 'x.y.z', 'x.y.z-rcN', 'x.y.z-alpha', or 'x.y.z-alpha-N'"
49+
}
50+
Version(major = major, minor = minor, patch = patch, rc = null, alpha = components[2].toInt())
51+
}
52+
else -> {
53+
throw IllegalArgumentException(
54+
"version '$version' does not follow the form 'x.y.z', 'x.y.z-rcN', 'x.y.z-alpha', or 'x.y.z-alpha-N'"
55+
)
56+
}
5457
}
5558
}
5659
}
5760

5861
override fun compareTo(other: Version): Int {
5962
val major = major.compareTo(other.major)
60-
if (major == 0) {
61-
val minor = minor.compareTo(other.minor)
62-
if (minor == 0) {
63-
val patch = patch.compareTo(other.patch)
64-
if (patch == 0) {
65-
if (alpha == null) {
66-
if (other.alpha == null) return 0
67-
else return 1
68-
} else {
69-
if (other.alpha != null) return alpha.compareTo(other.alpha)
70-
else return -1
71-
}
72-
} else return patch
73-
} else return minor
74-
} else return major
63+
if (major != 0) return major
64+
65+
val minor = minor.compareTo(other.minor)
66+
if (minor != 0) return minor
67+
68+
val patch = patch.compareTo(other.patch)
69+
if (patch != 0) return patch
70+
71+
if (rc != null) {
72+
if (other.rc != null) return rc.compareTo(other.rc)
73+
else if (other.alpha != null) return 1
74+
else return -1
75+
}
76+
77+
if (alpha != null) {
78+
if (other.alpha != null) return alpha.compareTo(other.alpha)
79+
else return -1
80+
}
81+
82+
return 1 // x.y.z is always after x.y.z-prereleases
7583
}
7684

7785
override fun toString(): String {
78-
val alpha =
79-
if (alpha != null) {
80-
if (alpha == 1) "-alpha"
81-
else "-alpha-$alpha"
82-
} else ""
83-
return "$major.$minor.$patch$alpha"
86+
val prerelease = when {
87+
rc != null -> "-rc$rc"
88+
alpha == 1 -> "-alpha"
89+
alpha != null -> "-alpha-$alpha"
90+
else -> ""
91+
}
92+
return "$major.$minor.$patch$prerelease"
8493
}
94+
95+
fun isPrerelease(): Boolean = rc != null || alpha != null
8596
}

0 commit comments

Comments
 (0)