cloud_bbb/scripts/imports/changelog.ts

150 lines
3.3 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require('fs');
const path = require('path');
const simpleGit = require('simple-git/promise');
const inquirer = require('inquirer');
const git = simpleGit();
export async function generateChangelog(version: string): Promise<string> {
const latestTag = (await git.tags()).latest;
const title = `v${version}` === latestTag ? '[Unreleased]' : `${version} (${new Date().toISOString().split('T')[0]})`;
const logs = await git.log({
from: latestTag,
to: 'HEAD',
});
const sections = [{
type: 'feat',
label: 'Added',
}, {
type: 'fix',
label: 'Fixed',
}];
const entries = {};
logs.all.forEach(log => {
const match = log.message.match(/^([a-z]+)(?:\((\w+)\))?: (.+)/);
if (!match) {
return;
}
const [, type, scope, description] = match;
const entry = { type, scope, description, issues: <string[]> [] };
if(log.body) {
const matches = log.body.match(/(?:fix|fixes|closes?|refs?) #(\d+)/g) || [];
for (const match of matches) {
const [, number] = <RegExpMatchArray> match.match(/(\d+)$/);
entry.issues.push(number);
}
}
if (!entries[type]) {
entries[type] = [];
}
entries[type].push(entry);
});
let changeLog = `## ${title}\n`;
function stringifyEntry(entry) {
const issues = entry.issues.map(issue => {
return `[#${issue}](https://github.com/sualko/cloud_bbb/issues/${issue})`;
}).join('');
return `- ${issues}${issues.length > 0 ? ' ' : ''}${entry.description}\n`;
}
sections.forEach(section => {
if (!entries[section.type]) {
return;
}
changeLog += `### ${section.label}\n`;
entries[section.type].forEach(entry => {
changeLog += stringifyEntry(entry);
});
delete entries[section.type];
changeLog += '\n';
});
const miscKeys = Object.keys(entries);
if (miscKeys && miscKeys.length > 0) {
changeLog += '### Misc\n';
miscKeys.forEach(type => {
entries[type].forEach(entry => {
changeLog += stringifyEntry(entry);
});
});
}
return changeLog;
}
export async function editChangeLog(changeLog: string):Promise<string> {
const answers = await inquirer.prompt([{
type: 'editor',
name: 'changeLog',
message: 'You have now the possibility to edit the change log',
default: changeLog,
}]);
return answers.changeLog;
}
export async function hasChangeLogEntry(version: string): Promise<boolean> {
if (!version) {
return false;
}
const entry = await getChangelogEntry(version);
if (entry.split('\n').filter(line => !!line.trim()).length < 2) {
throw `Found no change log entry for ${version}`;
}
return true;
}
export function getChangelogEntry(version: string): Promise<string> {
return new Promise<string>(resolve => {
fs.readFile(path.join(__dirname, '..', '..', 'CHANGELOG.md'), 'utf8', function (err, data) {
if (err) throw err;
const releaseHeader = /^\d+\.\d+\.\d+$/.test(version) ? `## ${version}` : '## [Unreleased]';
const lines = data.split('\n');
const entry: string[] = [];
let inEntry = false;
for(const line of lines) {
if (line.startsWith(releaseHeader)) {
inEntry = true;
} else if (line.startsWith('## ') && entry.length > 0) {
inEntry = false;
break;
}
if (inEntry) {
entry.push(line);
}
}
resolve(entry.join('\n'));
});
});
}