Skip to content

Commit

Permalink
Add automatic changelogs (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
DebugOk authored Sep 11, 2023
1 parent d94a0fc commit 6c21b29
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 0 deletions.
56 changes: 56 additions & 0 deletions .github/workflows/changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: PR Changelogs
concurrency: commit_action
on:
pull_request_target:
types: [closed]

env:
GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }}
CHANGELOG_DIR: ${{ vars.CHANGELOG_DIR }}
PR_NUMBER: ${{ github.event.number }}

jobs:
changelog:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
permissions:
contents: write
steps:
- name: Checkout Master
uses: actions/checkout@v3
with:
token: ${{ secrets.BOT_TOKEN }}
ref: "${{ vars.CHANGELOG_BRANCH }}"

- name: Setup Git
run: |
git config --global user.name "${{ vars.CHANGELOG_USER }}"
git config --global user.email "${{ vars.CHANGELOG_EMAIL }}"
shell: bash

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18.x

- name: Install Dependencies
run: |
cd "Tools/changelog"
npm install
shell: bash
continue-on-error: true

- name: Generate Changelog
run: |
cd "Tools/changelog"
node changelog.js
shell: bash
continue-on-error: true

- name: Commit Changelog
run: |
git add *.yml
git commit -m "${{ vars.CHANGELOG_MESSAGE }} (#${{ env.PR_NUMBER }})"
git push
shell: bash
continue-on-error: true
1 change: 1 addition & 0 deletions Resources/Changelog/DeltaVChangelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Entries:
153 changes: 153 additions & 0 deletions Tools/changelog/changelog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// 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}`;

// Regexes
const HeaderRegex = /^\s*(?::cl:|🆑) *([a-z0-9_\- ]+)?\s+/im; // :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;
}

// 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,
};

// 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(`../../${process.env.CHANGELOG_DIR}`, "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(`../../${process.env.CHANGELOG_DIR}`)) {
const file = fs.readFileSync(`../../${process.env.CHANGELOG_DIR}`, "utf8");
data = yaml.load(file);
}

data.Entries.push(entry);

// Write updated changelogs file
fs.writeFileSync(
`../../${process.env.CHANGELOG_DIR}`,
"Entries:\n" +
yaml.dump(data.Entries, { indent: 2 }).replace(/^---/, "")
);
}

// Run main
main();
7 changes: 7 additions & 0 deletions Tools/changelog/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "changelogs",
"dependencies": {
"axios": "^1.3.4",
"js-yaml": "^4.1.0"
}
}

0 comments on commit 6c21b29

Please sign in to comment.