diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index 163759e7513..6b5dc29c9b9 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -38,7 +38,6 @@ When the operator says “release”, immediately do this preflight (no extra qu 3. **Changelog & docs** - [ ] Update `CHANGELOG.md` with user-facing highlights (create the file if missing); keep entries strictly descending by version. - - Tip: use `pnpm changelog:add -- --section fixes --entry "Your entry. (#12345) Thanks @contrib."` (or `--section changes`) to append deterministically under the current Unreleased block. - [ ] Ensure README examples/flags match current CLI behavior (notably new commands or options). 4. **Validation** diff --git a/package.json b/package.json index 76d0868422f..66a60a5dc00 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "build": "pnpm canvas:a2ui:bundle && tsdown && pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-compat.ts", "build:plugin-sdk:dts": "tsc -p tsconfig.plugin-sdk.dts.json", "canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh", - "changelog:add": "node --import tsx scripts/changelog-add.ts", "check": "pnpm format:check && pnpm tsgo && pnpm lint", "check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:check-links", "check:loc": "node --import tsx scripts/check-ts-max-loc.ts --max 500", diff --git a/scripts/changelog-add.ts b/scripts/changelog-add.ts deleted file mode 100644 index 2422a00ef4b..00000000000 --- a/scripts/changelog-add.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { readFileSync, writeFileSync } from "node:fs"; -import { resolve } from "node:path"; - -export type UnreleasedSection = "changes" | "fixes"; - -function normalizeEntry(entry: string): string { - const trimmed = entry.trim(); - if (!trimmed) { - throw new Error("entry must not be empty"); - } - return trimmed.startsWith("- ") ? trimmed : `- ${trimmed}`; -} - -function sectionHeading(section: UnreleasedSection): string { - return section === "changes" ? "### Changes" : "### Fixes"; -} - -export function insertUnreleasedChangelogEntry( - changelogContent: string, - section: UnreleasedSection, - entry: string, -): string { - const normalizedEntry = normalizeEntry(entry); - const lines = changelogContent.split(/\r?\n/); - const unreleasedHeaderIndex = lines.findIndex((line) => - /^##\s+.+\s+\(Unreleased\)\s*$/.test(line.trim()), - ); - if (unreleasedHeaderIndex < 0) { - throw new Error("could not find an '(Unreleased)' changelog section"); - } - - const unreleasedEndIndex = lines.findIndex( - (line, index) => index > unreleasedHeaderIndex && /^##\s+/.test(line.trim()), - ); - const unreleasedLimit = unreleasedEndIndex < 0 ? lines.length : unreleasedEndIndex; - const sectionLabel = sectionHeading(section); - const sectionStartIndex = lines.findIndex( - (line, index) => - index > unreleasedHeaderIndex && index < unreleasedLimit && line.trim() === sectionLabel, - ); - if (sectionStartIndex < 0) { - throw new Error(`could not find '${sectionLabel}' under unreleased section`); - } - - const sectionEndIndex = lines.findIndex( - (line, index) => - index > sectionStartIndex && - index < unreleasedLimit && - (/^###\s+/.test(line.trim()) || /^##\s+/.test(line.trim())), - ); - const targetIndex = sectionEndIndex < 0 ? unreleasedLimit : sectionEndIndex; - let insertionIndex = targetIndex; - while (insertionIndex > sectionStartIndex + 1 && lines[insertionIndex - 1].trim() === "") { - insertionIndex -= 1; - } - - if ( - lines.slice(sectionStartIndex + 1, targetIndex).some((line) => line.trim() === normalizedEntry) - ) { - return changelogContent; - } - - lines.splice(insertionIndex, 0, normalizedEntry); - return `${lines.join("\n")}\n`; -} - -type CliArgs = { - section: UnreleasedSection; - entry: string; - file: string; -}; - -function parseCliArgs(argv: string[]): CliArgs { - let section: UnreleasedSection | null = null; - let entry = ""; - let file = "CHANGELOG.md"; - - for (let i = 0; i < argv.length; i += 1) { - const arg = argv[i]; - if (arg === "--section") { - const value = argv[i + 1]; - if (value !== "changes" && value !== "fixes") { - throw new Error("--section must be one of: changes, fixes"); - } - section = value; - i += 1; - continue; - } - if (arg === "--entry") { - entry = argv[i + 1] ?? ""; - i += 1; - continue; - } - if (arg === "--file") { - file = argv[i + 1] ?? file; - i += 1; - continue; - } - throw new Error(`unknown argument: ${arg}`); - } - - if (!section) { - throw new Error("missing --section "); - } - if (!entry.trim()) { - throw new Error("missing --entry "); - } - return { section, entry, file }; -} - -function runCli(): void { - const args = parseCliArgs(process.argv.slice(2)); - const changelogPath = resolve(process.cwd(), args.file); - const content = readFileSync(changelogPath, "utf8"); - const next = insertUnreleasedChangelogEntry(content, args.section, args.entry); - if (next !== content) { - writeFileSync(changelogPath, next, "utf8"); - } -} - -if (import.meta.url === `file://${process.argv[1]}`) { - runCli(); -} diff --git a/test/scripts/changelog-add.test.ts b/test/scripts/changelog-add.test.ts deleted file mode 100644 index f9c0d4755d8..00000000000 --- a/test/scripts/changelog-add.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { insertUnreleasedChangelogEntry } from "../../scripts/changelog-add.ts"; - -const SAMPLE = `# Changelog - -## 2026.2.24 (Unreleased) - -### Changes - -- Existing change. - -### Fixes - -- Existing fix. - -## 2026.2.23 - -### Changes - -- Older entry. -`; - -describe("changelog-add", () => { - it("inserts a new unreleased fixes entry before the next version section", () => { - const next = insertUnreleasedChangelogEntry( - SAMPLE, - "fixes", - "New fix entry. (#123) Thanks @someone.", - ); - expect(next).toContain( - "- Existing fix.\n- New fix entry. (#123) Thanks @someone.\n\n## 2026.2.23", - ); - }); - - it("normalizes missing bullet prefix", () => { - const next = insertUnreleasedChangelogEntry(SAMPLE, "changes", "New change."); - expect(next).toContain("- Existing change.\n- New change.\n\n### Fixes"); - }); - - it("does not duplicate identical entry", () => { - const once = insertUnreleasedChangelogEntry(SAMPLE, "fixes", "New fix."); - const twice = insertUnreleasedChangelogEntry(once, "fixes", "New fix."); - expect(twice).toBe(once); - }); -});