6
2026-01-13 18:31:02 +02:00

168 lines
4.4 KiB
JavaScript

// Dependencies
const fs = require("fs");
const yaml = require("js-yaml");
const axios = require("axios");
// Use GitHub token if available
if (process.env.GITHUB_TOKEN) axios.defaults.headers.common["Authorization"] = `Bearer ${process.env.GITHUB_TOKEN}`;
// Check changelog directory.
if (!process.env.CHANGELOG_DIR) {
console.log("CHANGELOG_DIR not defined, exiting.");
return process.exit(1);
}
const ChangelogFilePath = `../../../${process.env.CHANGELOG_DIR}`
// Regexes
const HeaderRegex = /^\s*(?::cl:|๐Ÿ†‘) *([a-z0-9_\-, ]+)?/img; // :cl: or ๐Ÿ†‘ [0] followed by optional author name [1]
const EntryRegex = /^ *[*-]? *(add|remove|tweak|fix): *([^\n\r]+)\r?$/img; // * or - followed by change type [0] and change message [1]
const CommentRegex = /<!--.*?-->/gs; // HTML comments
// Main function
async function main() {
// Get PR details
const pr = await axios.get(`https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pulls/${process.env.PR_NUMBER}`);
const { merged_at, body, user } = pr.data;
// Remove comments from the body
commentlessBody = body.replace(CommentRegex, '');
// Get author
const headerMatch = HeaderRegex.exec(commentlessBody);
if (!headerMatch) {
console.log("No changelog entry found, skipping");
return;
}
let author = headerMatch[1];
if (!author) {
console.log("No author found, setting it to author of the PR\n");
author = user.login;
} else {
author = author.trim()
}
// Offset results past the header
commentlessBody = commentlessBody.slice(HeaderRegex.lastIndex);
// Get all changes from the body
const entries = getChanges(commentlessBody);
// Time is something like 2021-08-29T20:00:00Z
// Time should be something like 2023-02-18T00:00:00.0000000+00:00
let time = merged_at;
if (time)
{
time = time.replace("z", ".0000000+00:00").replace("Z", ".0000000+00:00");
}
else
{
console.log("Pull request was not merged, skipping");
return;
}
// Construct changelog yml entry
const entry = {
author: author,
changes: entries,
id: getHighestCLNumber() + 1,
time: time,
url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/${process.env.PR_NUMBER}`,
};
// Write changelogs
writeChangelog(entry);
console.log(`Changelog updated with changes from PR #${process.env.PR_NUMBER}`);
}
// Code chunking
// Get all changes from the PR body
function getChanges(body) {
const matches = [];
const entries = [];
for (const match of body.matchAll(EntryRegex)) {
matches.push([match[1], match[2]]);
}
if (!matches)
{
console.log("No changes found, skipping");
return;
}
// Check change types and construct changelog entry
matches.forEach((entry) => {
let type;
switch (entry[0].toLowerCase()) {
case "add":
type = "Add";
break;
case "remove":
type = "Remove";
break;
case "tweak":
type = "Tweak";
break;
case "fix":
type = "Fix";
break;
default:
break;
}
if (type) {
entries.push({
type: type,
message: entry[1],
});
}
});
return entries;
}
// Get the highest changelog number from the changelogs file
function getHighestCLNumber() {
// Read changelogs file
const file = fs.readFileSync(ChangelogFilePath, "utf8");
// Get list of CL numbers
const data = yaml.load(file);
const entries = data && data.Entries ? Array.from(data.Entries) : [];
const clNumbers = entries.map((entry) => entry.id);
// Return highest changelog number
return Math.max(...clNumbers, 0);
}
function writeChangelog(entry) {
let data = { Entries: [] };
// Create a new changelogs file if it does not exist
if (fs.existsSync(ChangelogFilePath)) {
const file = fs.readFileSync(ChangelogFilePath, "utf8");
data = yaml.load(file);
}
data.Entries.push(entry);
// Write updated changelogs file
fs.writeFileSync(
ChangelogFilePath,
"Entries:\n" +
yaml.dump(data.Entries, { indent: 2 }).replace(/^---/, "")
);
}
// Run main
main();