Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/complete-release-notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---

Include CLI changes alongside VS Code changes in GitHub release notes.
38 changes: 37 additions & 1 deletion script/kilocode/release-notes.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,45 @@
import { expect, test } from "bun:test"
import { buildNotes } from "./release-notes"
import { buildNotes, buildReleaseNotes } from "./release-notes"

const section = (version: string, body: string) => `## ${version}\n\n${body}\n`
const release = (tagName: string, isPrerelease = false, isDraft = false) => ({ tagName, isPrerelease, isDraft })

test("release notes include VS Code and CLI changes with nested headings", () => {
expect(
buildReleaseNotes({
version: "7.5.16",
prerelease: false,
releases: [],
vscode: section("7.5.16", "### Minor Changes\n\n- Improve Agent Manager"),
cli: section("7.5.16", "### Patch Changes\n\n- #13896 Fix session list --all"),
}),
).toBe(
"## VS Code\n\n### 7.5.16\n\n#### Minor Changes\n\n- Improve Agent Manager\n\n" +
"## CLI\n\n### 7.5.16\n\n#### Patch Changes\n\n- #13896 Fix session list --all",
)
})

test("combined notes retain channel filtering for both packages", () => {
const input = {
version: "1.2.3",
releases: [release("v1.2.2", true), release("v1.2.1")],
vscode: section("1.2.3", "editor") + section("1.2.2", "editor preview") + section("1.2.1", "old editor"),
cli: section("1.2.3", "cli") + section("1.2.2", "cli preview") + section("1.2.1", "old cli"),
}
expect(buildReleaseNotes({ ...input, prerelease: true })).toBe("## VS Code\n\neditor\n\n## CLI\n\ncli")
expect(buildReleaseNotes({ ...input, prerelease: false })).toBe(
"## VS Code\n\n### 1.2.3\n\neditor\n\n### 1.2.2\n\neditor preview\n\n" +
"## CLI\n\n### 1.2.3\n\ncli\n\n### 1.2.2\n\ncli preview",
)
})

test("combined notes omit empty packages and use one fallback when both are empty", () => {
const input = { version: "1.2.3", prerelease: true, releases: [] }
expect(buildReleaseNotes({ ...input, vscode: "", cli: section("1.2.3", "fix") })).toBe("## CLI\n\nfix")
expect(buildReleaseNotes({ ...input, vscode: section("1.2.3", "fix"), cli: "" })).toBe("## VS Code\n\nfix")
expect(buildReleaseNotes({ ...input, vscode: "", cli: section("1.2.3", "") })).toBe("No notable changes")
})

test("prerelease notes contain only their own section", () => {
const changelog = section("1.2.3", "current prerelease") + section("1.2.2", "previous prerelease")
const notes = buildNotes({
Expand Down
29 changes: 27 additions & 2 deletions script/kilocode/release-notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,36 @@ export function buildNotes(input: { version: string; prerelease: boolean; releas
return notes.join("\n\n") || fallback
}

export function buildReleaseNotes(input: {
version: string
prerelease: boolean
releases: Release[]
vscode: string
cli: string
}) {
const notes = (
[
["VS Code", input.vscode],
["CLI", input.cli],
] as const
).flatMap(([name, changelog]) => {
const body = buildNotes({ ...input, changelog })
if (body === fallback) return []
return [`## ${name}\n\n${input.prerelease ? body : body.replace(/^(#+) /gm, "#$1 ")}`]
})
return notes.join("\n\n") || fallback
}

export async function publishNotes(input: { version: string; prerelease: boolean; repo?: string; temp?: string }) {
const repo = input.repo ? ["--repo", input.repo] : []
const releases: Release[] = await $`gh release list --limit 1000 --json tagName,isDraft,isPrerelease ${repo}`.json()
const changelog = await Bun.file(new URL("../../packages/kilo-vscode/CHANGELOG.md", import.meta.url)).text()
const body = buildNotes({ ...input, releases, changelog })
const [vscode, cli] = await Promise.all([
Bun.file(new URL("../../packages/kilo-vscode/CHANGELOG.md", import.meta.url)).text(),
// The inherited "opencode" folder contains Kilo's CLI (@kilocode/cli), not upstream release notes.
// Read Kilo's changelog, including compact upstream summaries added when those changes are merged into Kilo.
Bun.file(new URL("../../packages/opencode/CHANGELOG.md", import.meta.url)).text(),
])
const body = buildReleaseNotes({ ...input, releases, vscode, cli })
const notes = `${input.temp ?? "/tmp"}/release-notes.txt`
const target = input.version.startsWith("v") ? input.version : `v${input.version}`
const flags = input.prerelease ? ["--draft=false", "--prerelease"] : ["--draft=false"]
Expand Down
Loading