Skip to content

Commit

Permalink
Tools: Parse upgrade note from PR description
Browse files Browse the repository at this point in the history
  • Loading branch information
TorsteinHonsi committed Mar 27, 2020
1 parent cccf5a0 commit 02f2b53
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 50 deletions.
125 changes: 76 additions & 49 deletions changelog/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,34 @@ const childProcess = require('child_process');
path = require('path'),
tree = require('../tree.json');

/*
* Return a list of options so that we can auto-link option references in
* the changelog.
*/
function getOptionKeys() {
const keys = [];

function recurse(subtree, optionPath) {
Object.keys(subtree).forEach(key => {
if (optionPath + key !== '') {
// Push only the second level, we don't want auto linking of
// general words like chart, series, legend, tooltip etc.
if (optionPath.indexOf('.') !== -1) {
keys.push(optionPath + key);
}
if (subtree[key].children) {
recurse(subtree[key].children, `${optionPath}${key}.`);
}
}
});
}

recurse(tree, '');
return keys;
}

const optionKeys = getOptionKeys();

/**
* Get the log from Git
*/
Expand Down Expand Up @@ -137,10 +165,47 @@ const childProcess = require('child_process');
return washed;
}

function addAPILinks(str, apiFolder) {
let match;
const reg = /`([a-zA-Z0-9\.\[\]]+)`/g;

while ((match = reg.exec(str)) !== null) {

const shortKey = match[1];
let replacements = [];

optionKeys.forEach(longKey => {
if (longKey.indexOf(shortKey) !== -1) {
replacements.push(longKey);
}
});

// If more than one match, see if we can rule out children of
// objects
/*
if (replacements.length > 1) {
replacements = replacements.filter(
longKey => longKey.lastIndexOf(shortKey) === longKey.length - shortKey.length
);
}
*/

// If more than one match, we may be dealing with ambiguous keys
// like `formatter`, `lineWidth` etch.
if (replacements.length === 1) {
str = str.replace(
`\`${shortKey}\``,
`[${shortKey}](https://api.highcharts.com/${apiFolder}/${replacements[0]})`
);
}
}
return str;
}

/**
* Build the output
*/
function buildMarkdown(name, version, date, log, products, optionKeys) {
function buildMarkdown(name, version, date, log, products) {
var outputString,
filename = path.join(
__dirname,
Expand All @@ -160,6 +225,11 @@ const childProcess = require('child_process');
log = washLog(name, log);
}

const upgradeNotes = log
.filter(change => typeof change.upgradeNote === 'string')
.map(change => addAPILinks(`- ${change.upgradeNote}`, apiFolder))
.join('\n');

// Start the output string
outputString = '# Changelog for ' + name + ' v' + version + ' (' + date + ')\n\n';

Expand All @@ -168,33 +238,16 @@ const childProcess = require('child_process');
}
log.forEach((change, i) => {

let desc = change.description || change;
let match;
const reg = /`([a-zA-Z0-9\.\[\]]+)`/g;
const desc = addAPILinks(change.description || change, apiFolder);

while ((match = reg.exec(desc)) !== null) {

const shortKey = match[1];
const replacements = [];
// Start fixes
if (i === log.startFixes) {

optionKeys.forEach(longKey => {
if (longKey.indexOf(shortKey) !== -1) {
replacements.push(longKey);
}
});

// If more than one match, we may be dealing with ambiguous keys
// like `formatter`, `lineWidth` etch.
if (replacements.length === 1) {
desc = desc.replace(
`\`${shortKey}\``,
`[${shortKey}](https://api.highcharts.com/${apiFolder}/${replacements[0]})`
);
if (upgradeNotes) {
outputString += `\n## Upgrade notes\n${upgradeNotes}\n`;
}
}

// Start fixes
if (i === log.startFixes) {
outputString += '\n## Bug fixes\n';
}

Expand All @@ -216,31 +269,6 @@ const childProcess = require('child_process');
return outputString;
}

/*
* Return a list of options so that we can auto-link option references in
* the changelog.
*/
function getOptionKeys(treeroot) {
const keys = [];

function recurse(subtree, optionPath) {
Object.keys(subtree).forEach(key => {
if (optionPath + key !== '') {
// Push only the second level, we don't want auto linking of
// general words like chart, series, legend, tooltip etc.
if (optionPath.indexOf('.') !== -1) {
keys.push(optionPath + key);
}
if (subtree[key].children) {
recurse(subtree[key].children, `${optionPath}${key}.`);
}
}
});
}

recurse(treeroot, '');
return keys;
}

function pad(number, length, padder) {
return new Array(
Expand Down Expand Up @@ -272,7 +300,6 @@ const childProcess = require('child_process');
// Get the Git log
getLog(function (log) {

const optionKeys = getOptionKeys(tree);
const pack = require(path.join(__dirname, '/../package.json'));
const d = new Date();
const review = [];
Expand Down
13 changes: 13 additions & 0 deletions changelog/pr-log.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ const log = {
'Highcharts Gantt': {}
};

// Whenever the string 'Upgrade note' appears, the next paragraph is interpreted
// as the not
const parseUpgradeNote = p => {
const paragraphs = p.body.split('\n');
for (let i = 0; i < paragraphs.length; i++) {
if (/upgrade note/i.test(paragraphs[i])) {
return (paragraphs[i + 1] ? paragraphs[i + 1].trim() : void 0);
}
}
return void 0;
};

module.exports = async since => {

const included = [];
Expand Down Expand Up @@ -83,6 +95,7 @@ module.exports = async since => {
// Simplify
pulls = pulls.map(p => ({
description: p.body.split('\n')[0].trim(),
upgradeNote: parseUpgradeNote(p),
labels: p.labels,
number: p.number
}));
Expand Down
4 changes: 3 additions & 1 deletion changelog/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ The changelog is generated from PR descriptions. The following rules apply:
* Label the PR with `Product: Highstock`, `Product: Highmaps` etc. for specific
products. PRs with no product tags go into the Highcharts changelog.
* Only the first paragraph of the description is used. Anything below the first
line break `\n` is removed.
line break `\n` is removed (except upgrade notes).
* Upgrade notes are marked with `#### Upgrade note`, then the _next paragraph_ after
this will be parsed as an upgrade note into the changelog.
* For consistency, bug fixes should start with "Fixed #xxxx".
* Since the changelog refers to changes that were done by a past release, write
in past tense.
Expand Down

0 comments on commit 02f2b53

Please sign in to comment.