mirror of
https://github.com/github/codeql-action.git
synced 2026-10-29 17:17:15 +00:00
Add parseChangelog and renderChangelog
This commit is contained in:
@@ -4,15 +4,19 @@
|
||||
* Tests for `changelog.ts`.
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import * as assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import {
|
||||
EMPTY_CHANGELOG,
|
||||
getReleaseDateString,
|
||||
parseChangelog,
|
||||
processChangelogForBackports,
|
||||
renderChangelog,
|
||||
setVersionAndDate,
|
||||
} from "./changelog";
|
||||
import { CHANGELOG_FILE } from "./config";
|
||||
|
||||
const testDate = new Date(2026, 7, 14);
|
||||
|
||||
@@ -37,6 +41,14 @@ describe("setVersionAndDate", async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseChangelog + renderChangelog", async () => {
|
||||
await it("renderChangelog(parseChangelog(c)) == c", async () => {
|
||||
const actualChangelog = fs.readFileSync(CHANGELOG_FILE, "utf-8");
|
||||
const roundtrip = renderChangelog(parseChangelog(actualChangelog));
|
||||
assert.deepEqual(roundtrip.split("\n"), actualChangelog.split("\n"));
|
||||
});
|
||||
});
|
||||
|
||||
const testChangelog = `# CodeQL Action Changelog
|
||||
|
||||
## 4.12.3 - 14 Aug 2026
|
||||
|
||||
@@ -15,6 +15,22 @@ export const EMPTY_CHANGELOG = `# CodeQL Action Changelog
|
||||
|
||||
${NO_CHANGES_STR}`;
|
||||
|
||||
/**
|
||||
* Represents sections in a changelog.
|
||||
*/
|
||||
export interface ChangelogSection {
|
||||
headerLine: string;
|
||||
bodyLines: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a changelog.
|
||||
*/
|
||||
export interface Changelog {
|
||||
preamble: string[];
|
||||
sections: ChangelogSection[];
|
||||
}
|
||||
|
||||
/** Returns `date` formatted as `DD Mon YYYY`. */
|
||||
export function getReleaseDateString(today: Date = new Date()): string {
|
||||
return today.toLocaleDateString("en-GB", {
|
||||
@@ -60,6 +76,75 @@ export function setVersionAndDate(
|
||||
return content.replace(UNRELEASED_PLACEHOLDER, versionAndDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses `content` into a structured representation of a changelog.
|
||||
*
|
||||
* @param content The contents of the changelog file.
|
||||
*/
|
||||
export function parseChangelog(content: string): Changelog {
|
||||
const lines = content.split("\n");
|
||||
let i = 0;
|
||||
|
||||
const preamble: string[] = [];
|
||||
const sections: ChangelogSection[] = [];
|
||||
let currentSection: ChangelogSection | undefined = undefined;
|
||||
|
||||
// Process all lines of the input file.
|
||||
while (i < lines.length) {
|
||||
const line = lines[i];
|
||||
|
||||
// Sections of the changelog start with `## `.
|
||||
if (line.startsWith("## ")) {
|
||||
// We have discovered a new section. If `currentSection` is already defined,
|
||||
// then this marks the end of that section. Push it to the array of sections
|
||||
// in the changelog.
|
||||
if (currentSection !== undefined) {
|
||||
sections.push(currentSection);
|
||||
}
|
||||
|
||||
// Initialise the new section.
|
||||
currentSection = { headerLine: line, bodyLines: [] };
|
||||
} else if (currentSection !== undefined) {
|
||||
// Add lines between the section header and the next to the current section.
|
||||
currentSection.bodyLines.push(line);
|
||||
} else {
|
||||
// This is neither a section header nor are we in a section already,
|
||||
// so this line is part of the preamble.
|
||||
preamble.push(line);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// Push the current section to the array of completed sections, if there is
|
||||
// still one unfinished.
|
||||
if (currentSection !== undefined) {
|
||||
sections.push(currentSection);
|
||||
}
|
||||
|
||||
return { preamble, sections };
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines an array of lines into a single string by adding line breaks.
|
||||
*/
|
||||
export function unlines(lines: string[]): string {
|
||||
return `${lines.join("\n")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a given changelog to a string.
|
||||
*/
|
||||
export function renderChangelog(changelog: Changelog): string {
|
||||
let result = unlines(changelog.preamble);
|
||||
|
||||
for (const section of changelog.sections) {
|
||||
result += `\n${section.headerLine}\n${unlines(section.bodyLines)}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes changelog entries for a backport, converting version references
|
||||
* from the source major version to the target major version and filtering
|
||||
|
||||
Reference in New Issue
Block a user