From 1e562e997603b40bff336de9a609a45258bcefcd Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 1 Apr 2021 12:54:06 -0230 Subject: [PATCH] Prevent duplicate changelog entries (#10786) The changelog update script now prevents duplicate entries from being added. Specifically, it will ensure that if a PR has been referenced already in an entry, it will not add it again. This should prevent it from adding duplicate entries for changes that were cherry-picked into hotfix releases. Note that this duplication prevention only works for entries containing a PR number. We don't have any way to prevent duplicate entries yet in cases where we don't know the associated PR. We will be preventing this possibility entirely pretty soon in some upcoming release automation changes though, so I'm not concerned about this omission. --- development/auto-changelog.js | 44 +++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/development/auto-changelog.js b/development/auto-changelog.js index c032572c6..4a6c42bca 100755 --- a/development/auto-changelog.js +++ b/development/auto-changelog.js @@ -27,7 +27,7 @@ async function main() { `${mostRecentTag}..HEAD`, ]); - const changelogEntries = []; + const commitEntries = []; for (const commit of commitsSinceLastRelease) { const [subject] = await runCommand('git', [ 'show', @@ -36,20 +36,20 @@ async function main() { commit, ]); - let prefix; + let prNumber; let description = subject; // Squash & Merge: the commit subject is parsed as ` (#)` if (subject.match(/\(#\d+\)/u)) { - const [, prNumber] = subject.match(/\(#(\d+)\)/u); - prefix = `[#${prNumber}](${URL}/pull/${prNumber})`; + 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 // # from `, 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 [, prNumber] = subject.match(/#(\d+)\sfrom/u); - prefix = `[#${prNumber}](${URL}/pull/${prNumber})`; + const matchResults = subject.match(/#(\d+)\sfrom/u); + prNumber = matchResults[1]; const [firstLineOfBody] = await runCommand('git', [ 'show', '-s', @@ -61,10 +61,7 @@ async function main() { // Otherwise: // Normal commits: The commit subject is the description, and the PR ID is omitted. - const changelogEntry = prefix - ? `- ${prefix}: ${description}` - : `- ${description}`; - changelogEntries.push(changelogEntry); + commitEntries.push({ prNumber, description }); } const changelogFilename = path.resolve(__dirname, '..', 'CHANGELOG.md'); @@ -93,6 +90,33 @@ async function main() { }'`, ); } + + 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); + } + changelogLines.splice(releaseHeaderIndex + 1, 0, ...changelogEntries); const updatedChangelog = changelogLines.join('\n'); await fs.writeFile(changelogFilename, updatedChangelog);