fix(jetbrains): address release workflow review

This commit is contained in:
kirillk
2026-05-27 12:17:49 -04:00
parent a411ba9321
commit beb8a406f0
4 changed files with 47 additions and 11 deletions
+6 -1
View File
@@ -114,7 +114,12 @@ jobs:
- name: Render release notes
working-directory: packages/kilo-jetbrains
run: ./gradlew getChangelog --project-version "$VERSION" --no-header --no-empty-sections --output-file=build/release-notes.md
run: |
if ! grep -Fq "## [$VERSION]" CHANGELOG.md; then
echo "Missing packages/kilo-jetbrains/CHANGELOG.md entry for $VERSION. Create a release PR or add the changelog section before tagging." >&2
exit 1
fi
./gradlew getChangelog --project-version "$VERSION" --no-header --no-empty-sections --output-file=build/release-notes.md
env:
VERSION: ${{ steps.version.outputs.version }}
@@ -5,6 +5,8 @@ on:
pull_request:
types:
- closed
branches:
- main
permissions:
contents: write
+2
View File
@@ -42,6 +42,8 @@ The workflow chooses a changelog base automatically:
Use `from_tag` only to override this comparison range.
For the first stable JetBrains release, there may be no previous stable tag yet. In that case, pass the last RC or another reviewed JetBrains tag as `from_tag`.
## Review the PR
The workflow creates or updates a branch like:
+37 -10
View File
@@ -95,7 +95,7 @@ async function base(ver: string, kind: "rc" | "stable") {
.map((item) => ({ tag: item, ver: item.replace(/^jetbrains\/v/, "") }))
.filter((item) => semver.valid(item.ver))
const target = semver.parse(ver)!
const want = semver.parse(ver)!
const stable = tags
.filter((item) => !semver.prerelease(item.ver) && semver.lt(item.ver, ver))
.sort((a, b) => semver.rcompare(a.ver, b.ver))
@@ -110,7 +110,7 @@ async function base(ver: string, kind: "rc" | "stable") {
.filter((item) => {
const parsed = semver.parse(item.ver)
if (!parsed) return false
if (parsed.major !== target.major || parsed.minor !== target.minor || parsed.patch !== target.patch) return false
if (parsed.major !== want.major || parsed.minor !== want.minor || parsed.patch !== want.patch) return false
return Boolean(semver.prerelease(item.ver)) && semver.lt(item.ver, ver)
})
.sort((a, b) => semver.rcompare(a.ver, b.ver))
@@ -137,17 +137,41 @@ async function release(from: string, tag: string) {
function section(ver: string, notes: string) {
const date = new Date().toISOString().slice(0, 10)
const lines = bullets(notes)
return [`## [${ver}] - ${date}`, "", "### Changed", ...lines, ""].join("\n")
const groups = entries(notes)
const lines = [`## [${ver}] - ${date}`, ""]
for (const title of ["Added", "Fixed", "Changed"] as const) {
const items = groups.get(title)
if (!items?.length) continue
lines.push(`### ${title}`, ...items, "")
}
if (lines.length === 2) lines.push("### Changed", "- No notable changes.", "")
return lines.join("\n")
}
function bullets(notes: string) {
const lines = notes
function entries(notes: string) {
const groups = new Map<string, string[]>([
["Added", []],
["Fixed", []],
["Changed", []],
])
for (const line of notes
.split(/\r?\n/)
.map((item) => item.trim())
.filter((item) => item.startsWith("- ") || item.startsWith("* "))
.map((item) => `- ${item.slice(2).trim()}`)
return lines.length > 0 ? lines : ["- No notable changes."]
.map((item) => item.slice(2).trim())) {
if (line.startsWith("@") && line.includes(" made their first contribution ")) continue
const text = `- ${line}`
if (/^(feat|add)(\(.+\))?:/i.test(line)) {
groups.get("Added")!.push(text)
continue
}
if (/^(fix|bug)(\(.+\))?:/i.test(line)) {
groups.get("Fixed")!.push(text)
continue
}
groups.get("Changed")!.push(text)
}
return groups
}
async function writepkg(ver: string) {
@@ -157,7 +181,10 @@ async function writepkg(ver: string) {
}
async function writelog(ver: string, entry: string) {
const current = await Bun.file(log).text().catch(() => "# Changelog\n\n## [Unreleased]\n")
const current = await Bun.file(log).text().catch((err: NodeJS.ErrnoException) => {
if (err.code === "ENOENT") return "# Changelog\n\n## [Unreleased]\n"
throw err
})
const clean = current.replace(regex(ver), "").replace(/\n{3,}/g, "\n\n")
const marker = "## [Unreleased]"
if (!clean.includes(marker)) throw new Error("CHANGELOG.md must contain ## [Unreleased]")
@@ -167,7 +194,7 @@ async function writelog(ver: string, entry: string) {
function regex(ver: string) {
const safe = ver.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
return new RegExp(`\\n?## \\[${safe}\\][\\s\\S]*?(?=\\n## \\[|$)`, "m")
return new RegExp(`\\n?## \\[${safe}\\][\\s\\S]*?(?=\\n## \\[|$)`)
}
function body(ver: string, kind: string, from: string, tag: string, notes: string) {