blob: b97dd6c9d25afe48d4ba6a59d2fcd883da212d3a [file] [log] [blame] [edit]
#!/usr/bin/env kotlin
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import java.io.File
import java.util.concurrent.TimeUnit
import kotlin.system.exitProcess
fun getCommits(fromRevision: String, toRevision: String, path: String?): List<Commit> {
val cmd = "git rev-list --format=medium --ancestry-path $fromRevision..$toRevision ${path ?: "."}"
val process = ProcessBuilder(*(cmd.split(" ")).toTypedArray())
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.INHERIT)
.start()
process.waitFor(60, TimeUnit.MINUTES)
val commits = process.inputStream.bufferedReader().readText().split("\n\ncommit ")
return commits.mapNotNull { commit ->
val commitId = matchCommit.find(commit)?.groupValues?.let { it[1] } ?: return@mapNotNull null
val changeId = matchChangeId.find(commit)?.groupValues?.let { it[1] }
val relnote = matchRelnote.find(commit)?.groupValues?.let { it[1] }
val issues = matchIssue.findAll(commit).mapNotNull { it.groups[1]?.value }.toList()
return@mapNotNull Commit(commitId, changeId, relnote, issues)
}
}
val matchCommit = Regex("^([0-9a-f]+)\n", RegexOption.IGNORE_CASE)
val matchRelnote =
Regex(
"^\\s*Relnote:\\s+(\"{3}.+\"{3}|\".+\"|[^\\n]+)$",
setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE, RegexOption.DOT_MATCHES_ALL),
)
val matchChangeId = Regex("Change-Id:\\s+(I[0-9a-f]+)", RegexOption.IGNORE_CASE)
val matchIssue = Regex("(?:Bug|Fixes):\\s+(\\d+)", RegexOption.IGNORE_CASE)
data class Commit(
val commit: String,
val changeId: String?,
val relnote: String?,
val issues: List<String>,
)
fun commitToGitHubUrl(commit: String) = "https://github.com/JetBrains/kotlin/commit/$commit"
fun changeIdToGerritUrl(changeId: String) = "https://android-review.googlesource.com/q/$changeId"
fun issueToBuganizerUrl(issue: String): String = "https://issuetracker.google.com/issue/$issue"
fun Commit.asReleaseNote(): String {
val commitLink = "[${commit.substring(0, 7)}](${commitToGitHubUrl(commit)})"
val issueLinks = issues.map { issue -> "[b/$issue](${issueToBuganizerUrl(issue)})" }.joinToString(", ")
return "$commitLink $relnote $issueLinks"
}
if (args.isEmpty()) {
println(
"""
Usage: <from-tag> <to-tag> [<path>]
For example, to generate release notes for v2.0.0-RC2:
<script> v2.0.0-RC1 v2.0.0-RC2
""".trimIndent()
)
exitProcess(1)
}
val ignoreRelnotes = listOf("n/a")
val fromRevision = args[0]
val toRevision = args[1]
val path = args.getOrNull(2)
getCommits(fromRevision, toRevision, path)
.filterNot { it.relnote == null || ignoreRelnotes.contains(it.relnote.toLowerCase()) }
.map { it.asReleaseNote() }
.forEach { println(it) }