mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
1c5dcd6efc
Each changelog release now has category headers. The standard "keep a changelog" [1] categories are used, along with the addition of "Uncategorized" for any changes that have not yet been categorized. The changelog script has been updated to add this "Uncategorized" header if it isn't already present, and to place any new commits under this header. The changelog has been updated to property categorize each change in recent releases, and to place changes in older releases under the header "Uncategorized". [1]: https://keepachangelog.com/en/1.0.0/
176 lines
5.7 KiB
JavaScript
Executable File
176 lines
5.7 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
const fs = require('fs').promises;
|
|
const assert = require('assert').strict;
|
|
const path = require('path');
|
|
const { escapeRegExp } = require('lodash');
|
|
const { version } = require('../app/manifest/_base.json');
|
|
const runCommand = require('./lib/runCommand');
|
|
|
|
const URL = 'https://github.com/MetaMask/metamask-extension';
|
|
|
|
async function main() {
|
|
await runCommand('git', ['fetch', '--tags']);
|
|
|
|
const [mostRecentTagCommitHash] = await runCommand('git', [
|
|
'rev-list',
|
|
'--tags',
|
|
'--max-count=1',
|
|
]);
|
|
const [mostRecentTag] = await runCommand('git', [
|
|
'describe',
|
|
'--tags',
|
|
mostRecentTagCommitHash,
|
|
]);
|
|
assert.equal(mostRecentTag[0], 'v', 'Most recent tag should start with v');
|
|
|
|
const commitsSinceLastRelease = await runCommand('git', [
|
|
'rev-list',
|
|
`${mostRecentTag}..HEAD`,
|
|
]);
|
|
|
|
const commitEntries = [];
|
|
for (const commit of commitsSinceLastRelease) {
|
|
const [subject] = await runCommand('git', [
|
|
'show',
|
|
'-s',
|
|
'--format=%s',
|
|
commit,
|
|
]);
|
|
|
|
let prNumber;
|
|
let description = subject;
|
|
|
|
// Squash & Merge: the commit subject is parsed as `<description> (#<PR ID>)`
|
|
if (subject.match(/\(#\d+\)/u)) {
|
|
const matchResults = subject.match(/\(#(\d+)\)/u);
|
|
prNumber = matchResults[1];
|
|
description = subject.match(/^(.+)\s\(#\d+\)/u)[1];
|
|
// Merge: the PR ID is parsed from the git subject (which is of the form `Merge pull request
|
|
// #<PR ID> from <branch>`, and the description is assumed to be the first line of the body.
|
|
// If no body is found, the description is set to the commit subject
|
|
} else if (subject.match(/#\d+\sfrom/u)) {
|
|
const matchResults = subject.match(/#(\d+)\sfrom/u);
|
|
prNumber = matchResults[1];
|
|
const [firstLineOfBody] = await runCommand('git', [
|
|
'show',
|
|
'-s',
|
|
'--format=%b',
|
|
commit,
|
|
]);
|
|
description = firstLineOfBody || subject;
|
|
}
|
|
// Otherwise:
|
|
// Normal commits: The commit subject is the description, and the PR ID is omitted.
|
|
|
|
commitEntries.push({ prNumber, description });
|
|
}
|
|
|
|
const changelogFilename = path.resolve(__dirname, '..', 'CHANGELOG.md');
|
|
const changelog = await fs.readFile(changelogFilename, { encoding: 'utf8' });
|
|
const changelogLines = changelog.split('\n');
|
|
|
|
const prNumbersWithChangelogEntries = [];
|
|
for (const line of changelogLines) {
|
|
const matchResults = line.match(/- \[#(\d+)\]/u);
|
|
if (matchResults === null) {
|
|
continue;
|
|
}
|
|
const prNumber = matchResults[1];
|
|
prNumbersWithChangelogEntries.push(prNumber);
|
|
}
|
|
|
|
const changelogEntries = [];
|
|
for (const { prNumber, description } of commitEntries) {
|
|
if (prNumbersWithChangelogEntries.includes(prNumber)) {
|
|
continue;
|
|
}
|
|
|
|
let changelogEntry;
|
|
if (prNumber) {
|
|
const prefix = `[#${prNumber}](${URL}/pull/${prNumber})`;
|
|
changelogEntry = `- ${prefix}: ${description}`;
|
|
} else {
|
|
changelogEntry = `- ${description}`;
|
|
}
|
|
changelogEntries.push(changelogEntry);
|
|
}
|
|
|
|
if (changelogEntries.length === 0) {
|
|
console.log('CHANGELOG required no updates');
|
|
return;
|
|
}
|
|
|
|
// remove the "v" prefix
|
|
const mostRecentVersion = mostRecentTag.slice(1);
|
|
|
|
const isReleaseCandidate = mostRecentVersion !== version;
|
|
const versionHeader = `## [${version}]`;
|
|
const escapedVersionHeader = escapeRegExp(versionHeader);
|
|
const currentDevelopBranchHeader = '## [Unreleased]';
|
|
const currentReleaseHeaderPattern = isReleaseCandidate
|
|
? // This ensures this doesn't match on a version with a suffix
|
|
// e.g. v9.0.0 should not match on the header v9.0.0-beta.0
|
|
`${escapedVersionHeader}$|${escapedVersionHeader}\\s`
|
|
: escapeRegExp(currentDevelopBranchHeader);
|
|
|
|
let releaseHeaderIndex = changelogLines.findIndex((line) =>
|
|
line.match(new RegExp(currentReleaseHeaderPattern, 'u')),
|
|
);
|
|
if (releaseHeaderIndex === -1) {
|
|
if (!isReleaseCandidate) {
|
|
throw new Error(
|
|
`Failed to find release header '${currentDevelopBranchHeader}'`,
|
|
);
|
|
}
|
|
|
|
// Add release header if not found
|
|
const firstReleaseHeaderIndex = changelogLines.findIndex((line) =>
|
|
line.match(/## \[\d+\.\d+\.\d+\]/u),
|
|
);
|
|
const [, previousVersion] = changelogLines[firstReleaseHeaderIndex].match(
|
|
/## \[(\d+\.\d+\.\d+)\]/u,
|
|
);
|
|
changelogLines.splice(firstReleaseHeaderIndex, 0, versionHeader, '');
|
|
releaseHeaderIndex = firstReleaseHeaderIndex;
|
|
|
|
// Update release link reference definitions
|
|
// A link reference definition is added for the new release, and the
|
|
// "Unreleased" header is updated to point at the range of commits merged
|
|
// after the most recent release.
|
|
const unreleasedLinkIndex = changelogLines.findIndex((line) =>
|
|
line.match(/\[Unreleased\]:/u),
|
|
);
|
|
changelogLines.splice(
|
|
unreleasedLinkIndex,
|
|
1,
|
|
`[Unreleased]: ${URL}/compare/v${version}...HEAD`,
|
|
`[${version}]: ${URL}/compare/v${previousVersion}...v${version}`,
|
|
);
|
|
}
|
|
|
|
// Ensure "Uncategorized" header is present
|
|
const uncategorizedHeaderIndex = releaseHeaderIndex + 1;
|
|
const uncategorizedHeader = '### Uncategorized';
|
|
if (changelogLines[uncategorizedHeaderIndex] !== '### Uncategorized') {
|
|
const hasEmptyLine = changelogLines[uncategorizedHeaderIndex] === '';
|
|
changelogLines.splice(
|
|
uncategorizedHeaderIndex,
|
|
0,
|
|
uncategorizedHeader,
|
|
// Ensure an empty line follows the new header
|
|
...(hasEmptyLine ? [] : ['']),
|
|
);
|
|
}
|
|
|
|
changelogLines.splice(uncategorizedHeaderIndex + 1, 0, ...changelogEntries);
|
|
const updatedChangelog = changelogLines.join('\n');
|
|
await fs.writeFile(changelogFilename, updatedChangelog);
|
|
|
|
console.log('CHANGELOG updated');
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|