From f5440b7c7fd181116dea50e2411d04c80945b8ce Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 15 Jun 2026 17:14:09 +0200 Subject: [PATCH] fix: filter upstream changeset notes --- script/upstream/README.md | 4 +- script/upstream/opencode-changesets.test.ts | 41 ++++++++++++++++++++- script/upstream/opencode-changesets.ts | 41 ++++++++++++++++++++- 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/script/upstream/README.md b/script/upstream/README.md index c136db668b..557d064b29 100644 --- a/script/upstream/README.md +++ b/script/upstream/README.md @@ -69,7 +69,7 @@ After merging upstream opencode releases, use `opencode-changesets.ts` to turn t bun script/upstream/opencode-changesets.ts 1.17.0 1.17.7 ``` -The script fetches releases from `anomalyco/opencode`, selects published releases in the semver range `(from, to]`, and writes one `.changeset/opencode-vX-Y-Z.md` file per release. It defaults to patch changesets for the fixed release group, `@kilocode/cli` and `kilo-code`. +The script fetches releases from `anomalyco/opencode`, selects published releases in the semver range `(from, to]`, and writes one `.changeset/opencode-vX-Y-Z.md` file per release. It defaults to patch changesets for the fixed release group, `@kilocode/cli` and `kilo-code`. Generated notes omit contributor thank-you blocks and the upstream `Desktop` section by default because Kilo does not ship the opencode desktop app. Preview without writing files: @@ -84,6 +84,8 @@ Useful options: | `--repo ` | Fetch releases from another GitHub repository | | `--package ` | Add a changeset package; repeat for multiple packages | | `--bump ` | Set the generated changeset bump type | +| `--drop-section ` | Omit a markdown `##` section by heading; repeat for multiple sections | +| `--no-default-drop-section` | Disable the default dropped sections, such as `Desktop` | | `--force` | Overwrite existing generated changeset files | | `--include-prerelease` | Include prerelease GitHub releases | diff --git a/script/upstream/opencode-changesets.test.ts b/script/upstream/opencode-changesets.test.ts index ca213ec170..9d2f75fcbd 100644 --- a/script/upstream/opencode-changesets.test.ts +++ b/script/upstream/opencode-changesets.test.ts @@ -29,17 +29,56 @@ describe("opencode changesets", () => { changeset({ tag_name: "v1.2.2", body: "\r\n## Core\r\n\r\n- Fix issue\r\n" }, { packages: ["@kilocode/cli", "kilo-code"], bump: "patch", + drop: ["Desktop"], }), ).toBe(`--- "@kilocode/cli": patch "kilo-code": patch --- -Integrate upstream opencode v1.2.2 release notes. +Changes from opencode v1.2.2 upstream: ## Core - Fix issue +`) + }) + + test("filters ignored sections and contributor thanks", () => { + expect( + changeset({ + tag_name: "v1.2.2", + body: `## Core + +- Keep this + +## Desktop + +- Drop this + +## Misc + +- Drop misc + +**Thank you to 1 community contributor:** + +- @user: + - Helped +`, + }, { + packages: ["@kilocode/cli"], + bump: "patch", + drop: ["Desktop", "Misc"], + }), + ).toBe(`--- +"@kilocode/cli": patch +--- + +Changes from opencode v1.2.2 upstream: + +## Core + +- Keep this `) }) }) diff --git a/script/upstream/opencode-changesets.ts b/script/upstream/opencode-changesets.ts index 59fe27c837..b815549077 100644 --- a/script/upstream/opencode-changesets.ts +++ b/script/upstream/opencode-changesets.ts @@ -21,6 +21,7 @@ type Opts = { root: string packages: string[] bump: Bump + drop: string[] dry: boolean force: boolean prerelease: boolean @@ -37,6 +38,8 @@ Options: --repo GitHub repository (default: anomalyco/opencode) --package Changeset package (repeatable; defaults to @kilocode/cli and kilo-code) --bump Changeset bump type: major, minor, patch (default: patch) + --drop-section Omit a markdown ## section by heading (repeatable; defaults to Desktop) + --no-default-drop-section Do not drop the default Desktop section --dry-run Print changesets without writing files --force Overwrite existing generated changeset files --include-prerelease Include prerelease GitHub releases @@ -76,6 +79,36 @@ function body(release: Release) { return `Integrate upstream opencode ${release.tag_name}.` } +function filter(input: string, drop: string[]) { + const drops = new Set(drop.map((item) => item.trim().toLowerCase()).filter(Boolean)) + const lines = input.replace(/\r\n?/g, "\n").split("\n") + const out: string[] = [] + let section = false + let thanks = false + + for (const line of lines) { + const match = line.match(/^##\s+(.+?)\s*$/) + if (match) { + section = drops.has(match[1].trim().toLowerCase()) + thanks = false + } + + if (line.match(/^\*\*Thank you to \d+ community contributors?:\*\*\s*$/)) { + thanks = true + continue + } + + if (thanks) { + if (!line.startsWith("-") && !line.startsWith(" -") && line.trim() !== "") thanks = false + if (thanks) continue + } + + if (!section) out.push(line) + } + + return out.join("\n").replace(/\n{3,}/g, "\n\n").trim() +} + function isRelease(input: unknown): input is Release { return Boolean(input && typeof input === "object" && "tag_name" in input && typeof input.tag_name === "string") } @@ -100,8 +133,9 @@ export function select(releases: Release[], from: string, to: string, prerelease .map((item) => ({ ...item.release, tag_name: tag(item.version) })) } -export function changeset(release: Release, opts: Pick) { - return `---\n${packages(opts.packages, opts.bump)}\n---\n\nIntegrate upstream opencode ${release.tag_name} release notes.\n\n${body(release)}\n` +export function changeset(release: Release, opts: Pick) { + const text = filter(body(release), opts.drop) || "No upstream release notes were published." + return `---\n${packages(opts.packages, opts.bump)}\n---\n\nChanges from opencode ${release.tag_name} upstream:\n\n${text}\n` } async function all(repo: string) { @@ -136,6 +170,8 @@ function opts() { repo: { type: "string", default: "anomalyco/opencode" }, package: { type: "string", multiple: true }, bump: { type: "string", default: "patch" }, + "drop-section": { type: "string", multiple: true }, + "no-default-drop-section": { type: "boolean", default: false }, "dry-run": { type: "boolean", default: false }, force: { type: "boolean", default: false }, "include-prerelease": { type: "boolean", default: false }, @@ -163,6 +199,7 @@ function opts() { root: path.resolve(import.meta.dir, "../.."), packages: parsed.values.package?.length ? parsed.values.package : ["@kilocode/cli", "kilo-code"], bump, + drop: [...(parsed.values["no-default-drop-section"] ? [] : ["Desktop"]), ...(parsed.values["drop-section"] ?? [])], dry: parsed.values["dry-run"], force: parsed.values.force, prerelease: parsed.values["include-prerelease"],