mirror of https://github.com/sualko/cloud_bbb
150 lines
3.3 KiB
TypeScript
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'));
|
|
});
|
|
});
|
|
}
|