mirror of
https://github.com/github/codeql-action.git
synced 2026-10-03 09:14:58 +00:00
Add CHANGELOG function addBodyLinesToUnreleasedSection
This will be used by the `pr-checks/changenotes.mts` script to "compile" the latest release entry of CHANGELOG.md.
This commit is contained in:
@@ -9,13 +9,17 @@ import * as fs from "node:fs";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import {
|
||||
addBodyLinesToUnreleasedSection,
|
||||
ChangelogSection,
|
||||
EMPTY_CHANGELOG,
|
||||
getHeader,
|
||||
getReleaseDateString,
|
||||
NO_CHANGES_STR,
|
||||
parseChangelog,
|
||||
processChangelogForBackports,
|
||||
renderChangelog,
|
||||
setVersionAndDate,
|
||||
UNRELEASED_PLACEHOLDER,
|
||||
} from "./changelog";
|
||||
import { CHANGELOG_FILE } from "./config";
|
||||
|
||||
@@ -89,3 +93,78 @@ describe("processChangelogForBackports", async () => {
|
||||
assert.deepEqual(result.split("\n"), testChangelogResult.split("\n"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("addBodyLinesToUnreleasedSection", async () => {
|
||||
function newChangelogWithSections(sections: ChangelogSection[]) {
|
||||
return {
|
||||
preamble: [],
|
||||
sections,
|
||||
};
|
||||
}
|
||||
|
||||
await it("throws error if '[UNRELEASED]' section does not exist", async () => {
|
||||
const emptyChangelog = newChangelogWithSections([]);
|
||||
assert.throws(() => addBodyLinesToUnreleasedSection(emptyChangelog, []));
|
||||
|
||||
const releasedChangelog = newChangelogWithSections([
|
||||
{
|
||||
headerLine: "## Release 1.0.0",
|
||||
bodyLines: [],
|
||||
},
|
||||
{
|
||||
headerLine: "## Release 2.0.0",
|
||||
bodyLines: [],
|
||||
},
|
||||
{
|
||||
headerLine: "## Release 3.0.0",
|
||||
bodyLines: [],
|
||||
},
|
||||
]);
|
||||
assert.throws(() => addBodyLinesToUnreleasedSection(releasedChangelog, []));
|
||||
});
|
||||
|
||||
await it("overwrites 'No user facing changes.'", async () => {
|
||||
const changelog = newChangelogWithSections([
|
||||
{
|
||||
headerLine: `## ${UNRELEASED_PLACEHOLDER}`,
|
||||
bodyLines: ["", NO_CHANGES_STR, ""],
|
||||
},
|
||||
]);
|
||||
|
||||
addBodyLinesToUnreleasedSection(changelog, ["- foo"]);
|
||||
|
||||
assert.equal(changelog.sections[0].bodyLines.length, 3);
|
||||
assert.deepEqual(changelog.sections[0].bodyLines, ["", "- foo", ""]);
|
||||
});
|
||||
|
||||
await it("does nothing if lines is empty", async () => {
|
||||
const changelog = newChangelogWithSections([
|
||||
{
|
||||
headerLine: `## ${UNRELEASED_PLACEHOLDER}`,
|
||||
bodyLines: ["", NO_CHANGES_STR, ""],
|
||||
},
|
||||
]);
|
||||
const changelogClone = structuredClone(changelog);
|
||||
|
||||
addBodyLinesToUnreleasedSection(changelog, []);
|
||||
|
||||
assert.deepEqual(changelog, changelogClone);
|
||||
});
|
||||
|
||||
await it("inserts a line", async () => {
|
||||
const changelog = newChangelogWithSections([
|
||||
{
|
||||
headerLine: `## ${UNRELEASED_PLACEHOLDER}`,
|
||||
bodyLines: ["", "- Added a new dependency.", ""],
|
||||
},
|
||||
]);
|
||||
const lineToInsert = "- foo";
|
||||
|
||||
addBodyLinesToUnreleasedSection(changelog, [lineToInsert]);
|
||||
|
||||
assert.equal(changelog.sections[0].bodyLines.length, 4);
|
||||
assert.ok(
|
||||
changelog.sections[0].bodyLines.some((line) => line === lineToInsert),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,6 +132,59 @@ export function parseChangelog(content: string): Changelog {
|
||||
return { preamble, sections };
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the changenotes `notes` under the `[UNRELEASED]` section of `changelog`.
|
||||
* If the section contains the stock message {@link NO_CHANGES_STR}, then
|
||||
* `notes` will be inserted in place and the stock message will be deleted.
|
||||
*
|
||||
* This function will throw an exception if `[UNRELEASED]` does not exist.
|
||||
*
|
||||
* @param changelog The CHANGELOG object to modify.
|
||||
* @param lines The changenotes to insert.
|
||||
*/
|
||||
export function addBodyLinesToUnreleasedSection(
|
||||
changelog: Changelog,
|
||||
lines: string[],
|
||||
) {
|
||||
// Find the '[UNRELEASED]' section.
|
||||
let unreleasedSection: ChangelogSection | undefined;
|
||||
for (const section of changelog.sections) {
|
||||
if (getHeader(section.headerLine) === UNRELEASED_PLACEHOLDER) {
|
||||
unreleasedSection = section;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the '[UNRELEASED]' section exists first.
|
||||
if (unreleasedSection === undefined) {
|
||||
throw Error(
|
||||
"Cannot put changenotes into CHANGELOG.md's '[UNRELEASED]' section because it does not exist",
|
||||
);
|
||||
}
|
||||
|
||||
let insertAtIndex = 0;
|
||||
let deleteCount = 0;
|
||||
|
||||
// If the section contains an empty line, preserve it -- insert afterward.
|
||||
if (
|
||||
unreleasedSection.bodyLines.length > 0 &&
|
||||
unreleasedSection.bodyLines[0] === ""
|
||||
) {
|
||||
insertAtIndex++;
|
||||
}
|
||||
|
||||
// If the section contains the stock message 'No user facing changes.'
|
||||
if (
|
||||
lines.length > 0 &&
|
||||
unreleasedSection.bodyLines.length > insertAtIndex &&
|
||||
unreleasedSection.bodyLines[insertAtIndex].trim() === NO_CHANGES_STR
|
||||
) {
|
||||
deleteCount++; // Delete the line by incrementing the delete marker.
|
||||
}
|
||||
|
||||
unreleasedSection.bodyLines.splice(insertAtIndex, deleteCount, ...lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines an array of lines into a single string by adding line breaks.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user