ci(release): share changelog release notes
This commit is contained in:
parent
ae08c3cc10
commit
f8073df320
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@ -76,19 +76,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
NOTES_FILE="$(mktemp)"
|
||||
RELEASE_VERSION="${RELEASE_VERSION}" node <<'NODE' > "${NOTES_FILE}"
|
||||
const fs = require("fs");
|
||||
const version = process.env.RELEASE_VERSION;
|
||||
const lines = fs.readFileSync("CHANGELOG.md", "utf8").split(/\r?\n/);
|
||||
const start = lines.findIndex((line) => line.startsWith(`## ${version} - `));
|
||||
if (start === -1) {
|
||||
console.error(`CHANGELOG.md is missing a ${version} release section`);
|
||||
process.exit(1);
|
||||
}
|
||||
const end = lines.findIndex((line, index) => index > start && line.startsWith("## "));
|
||||
const body = lines.slice(start + 1, end === -1 ? lines.length : end).join("\n").trim();
|
||||
process.stdout.write(`## plugin-inspector v${version}\n\n${body}\n`);
|
||||
NODE
|
||||
node scripts/release-notes.mjs "${RELEASE_VERSION}" > "${NOTES_FILE}"
|
||||
|
||||
release_flags=(--title "plugin-inspector ${RELEASE_TAG}" --notes-file "${NOTES_FILE}" --verify-tag)
|
||||
if [[ "${RELEASE_TAG}" =~ (alpha|beta|rc) ]]; then
|
||||
|
||||
@ -73,6 +73,12 @@ versioned section like `## 0.3.1 - 2026-04-28`, update `package.json` to the
|
||||
same version, and update Crabpot's `pluginInspectorRef` to the exact release
|
||||
commit.
|
||||
|
||||
Draft release notes from the current `Unreleased` section with:
|
||||
|
||||
```bash
|
||||
npm run release:notes
|
||||
```
|
||||
|
||||
## Crabpot follow-through
|
||||
|
||||
Before tagging a release, update Crabpot's `pluginInspectorRef` to the
|
||||
|
||||
@ -52,6 +52,7 @@
|
||||
"release:crabpot": "node scripts/check-crabpot-followthrough.mjs",
|
||||
"release:readiness": "npm run release:local && npm run release:crabpot",
|
||||
"release:local": "npm run check",
|
||||
"release:notes": "node scripts/release-notes.mjs --unreleased",
|
||||
"test": "node --test test/*.test.js"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
66
scripts/release-notes.mjs
Normal file
66
scripts/release-notes.mjs
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
const changelogText = readFileSync(options.changelogPath, "utf8");
|
||||
process.stdout.write(extractReleaseNotes({ changelogText, version: options.version }));
|
||||
}
|
||||
|
||||
export function extractReleaseNotes({ changelogText, version }) {
|
||||
const lines = changelogText.split(/\r?\n/);
|
||||
const start = findReleaseStart(lines, version);
|
||||
if (start === -1) {
|
||||
throw new Error(`CHANGELOG.md is missing a ${version} release section`);
|
||||
}
|
||||
|
||||
const end = lines.findIndex((line, index) => index > start && line.startsWith("## "));
|
||||
const body = lines.slice(start + 1, end === -1 ? lines.length : end).join("\n").trim();
|
||||
const title = version === "Unreleased" ? "plugin-inspector unreleased" : `plugin-inspector v${version}`;
|
||||
return `## ${title}\n\n${body}\n`;
|
||||
}
|
||||
|
||||
function findReleaseStart(lines, version) {
|
||||
if (version === "Unreleased") {
|
||||
return lines.findIndex((line) => line.trim() === "## Unreleased");
|
||||
}
|
||||
return lines.findIndex((line) => line.startsWith(`## ${version} - `));
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = {
|
||||
changelogPath: path.resolve("CHANGELOG.md"),
|
||||
version: undefined,
|
||||
};
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
if (arg === "--changelog") {
|
||||
options.changelogPath = path.resolve(argv[index + 1]);
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--unreleased") {
|
||||
options.version = "Unreleased";
|
||||
continue;
|
||||
}
|
||||
if (arg === "--version") {
|
||||
options.version = argv[index + 1];
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (!options.version) {
|
||||
options.version = arg;
|
||||
continue;
|
||||
}
|
||||
throw new Error(`unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
if (!options.version) {
|
||||
throw new Error("usage: release-notes.mjs <version>|--unreleased [--changelog CHANGELOG.md]");
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
38
test/release-notes.test.js
Normal file
38
test/release-notes.test.js
Normal file
@ -0,0 +1,38 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { extractReleaseNotes } from "../scripts/release-notes.mjs";
|
||||
|
||||
const changelog = [
|
||||
"# Changelog",
|
||||
"",
|
||||
"## Unreleased",
|
||||
"",
|
||||
"### Added",
|
||||
"",
|
||||
"- Add grouped API helpers.",
|
||||
"",
|
||||
"## 0.3.0 - 2026-04-27",
|
||||
"",
|
||||
"### Changed",
|
||||
"",
|
||||
"- Improve setup.",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
test("release notes can render unreleased draft notes", () => {
|
||||
assert.equal(
|
||||
extractReleaseNotes({ changelogText: changelog, version: "Unreleased" }),
|
||||
["## plugin-inspector unreleased", "", "### Added", "", "- Add grouped API helpers.", ""].join("\n"),
|
||||
);
|
||||
});
|
||||
|
||||
test("release notes can render versioned changelog sections", () => {
|
||||
assert.equal(
|
||||
extractReleaseNotes({ changelogText: changelog, version: "0.3.0" }),
|
||||
["## plugin-inspector v0.3.0", "", "### Changed", "", "- Improve setup.", ""].join("\n"),
|
||||
);
|
||||
});
|
||||
|
||||
test("release notes fail when a version section is missing", () => {
|
||||
assert.throws(() => extractReleaseNotes({ changelogText: changelog, version: "9.9.9" }), /missing a 9\.9\.9/);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user