Skip to content

Commit b8cf803

Browse files
authored
fix: only use latest template-oss specific commits in changelog (#430)
1 parent 5d806ab commit b8cf803

File tree

2 files changed

+107
-19
lines changed

2 files changed

+107
-19
lines changed

lib/release/changelog.js

+54-13
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class ChangelogNotes {
110110
return authorsByCommit
111111
}
112112

113-
async #getPullRequestForCommits (commits) {
113+
async #getPullRequestNumbersForCommits (commits) {
114114
const shas = commits
115115
.filter(c => !c.pullRequest?.number)
116116
.map(c => c.sha)
@@ -134,7 +134,7 @@ class ChangelogNotes {
134134
return pullRequestsByCommit
135135
}
136136

137-
#buildEntry (commit, { authors = [], pullRequest }) {
137+
#buildEntry (commit) {
138138
const entry = []
139139

140140
if (commit.sha) {
@@ -143,7 +143,7 @@ class ChangelogNotes {
143143
}
144144

145145
// A link to the pull request if the commit has one
146-
const commitPullRequest = commit.pullRequest?.number ?? pullRequest
146+
const commitPullRequest = commit.pullRequestNumber
147147
if (commitPullRequest) {
148148
entry.push(link(`#${commitPullRequest}`, this.#ghUrl('pull', commitPullRequest)))
149149
}
@@ -154,21 +154,65 @@ class ChangelogNotes {
154154
entry.push([scope, subject].filter(Boolean).join(' '))
155155

156156
// A list og the authors github handles or names
157-
if (authors.length) {
158-
entry.push(`(${authors.join(', ')})`)
157+
if (commit.authors.length) {
158+
entry.push(`(${commit.authors.join(', ')})`)
159159
}
160160

161161
return entry.join(' ')
162162
}
163163

164-
async buildNotes (commits, { version, previousTag, currentTag, changelogSections }) {
164+
#filterCommits (commits) {
165+
const filteredCommits = []
166+
const keyedDuplicates = {}
167+
168+
// Filter certain commits so we can make sure only the latest version of
169+
// each one gets into the changelog
170+
for (const commit of commits) {
171+
if (commit.bareMessage.startsWith('postinstall for dependabot template-oss PR')) {
172+
keyedDuplicates.templateOssPostInstall ??= []
173+
keyedDuplicates.templateOssPostInstall.push(commit)
174+
continue
175+
}
176+
177+
if (commit.bareMessage.startsWith('bump @npmcli/template-oss from')) {
178+
keyedDuplicates.templateOssBump ??= []
179+
keyedDuplicates.templateOssBump.push(commit)
180+
continue
181+
}
182+
183+
filteredCommits.push(commit)
184+
}
185+
186+
// Sort all our duplicates so we get the latest verion (by PR number) of each type.
187+
// Then flatten so we can put them all back into the changelog
188+
const sortedDupes = Object.values(keyedDuplicates)
189+
.filter((items) => Boolean(items.length))
190+
.map((items) => items.sort((a, b) => b.pullRequestNumber - a.pullRequestNumber))
191+
.flatMap(items => items[0])
192+
193+
// This moves them to the bottom of their changelog section which is not
194+
// strictly necessary but it's easier to do this way.
195+
for (const duplicate of sortedDupes) {
196+
filteredCommits.push(duplicate)
197+
}
198+
199+
return filteredCommits
200+
}
201+
202+
async buildNotes (rawCommits, { version, previousTag, currentTag, changelogSections }) {
165203
// get authors for commits for each sha
166-
const authorsByCommit = await this.#getAuthorsForCommits(commits)
204+
const authors = await this.#getAuthorsForCommits(rawCommits)
167205

168206
// when rebase merging multiple commits with a single PR, only the first commit
169207
// will have a pr number when coming from release-please. this check will manually
170208
// lookup commits without a pr number and find one if it exists
171-
const pullRequestByCommit = await this.#getPullRequestForCommits(commits)
209+
const prNumbers = await this.#getPullRequestNumbersForCommits(rawCommits)
210+
211+
const fullCommits = rawCommits.map((commit) => {
212+
commit.authors = authors[commit.sha] ?? []
213+
commit.pullRequestNumber = Number(commit.pullRequest?.number ?? prNumbers[commit.sha])
214+
return commit
215+
})
172216

173217
const changelog = new Changelog({
174218
version,
@@ -178,12 +222,9 @@ class ChangelogNotes {
178222
sections: changelogSections,
179223
})
180224

181-
for (const commit of commits) {
225+
for (const commit of this.#filterCommits(fullCommits)) {
182226
// Collect commits by type
183-
changelog.add(commit.type, this.#buildEntry(commit, {
184-
authors: authorsByCommit[commit.sha],
185-
pullRequest: pullRequestByCommit[commit.sha],
186-
}))
227+
changelog.add(commit.type, this.#buildEntry(commit))
187228

188229
// And breaking changes to its own section
189230
changelog.add(Changelog.BREAKING, ...commit.notes

test/release/changelog.js

+53-6
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ const mockGitHub = ({ commits, authors }) => ({
3636
},
3737
})
3838

39-
const mockChangelog = async ({ shas = true, authors = true, previousTag = true } = {}) => {
40-
const commits = [{
39+
const mockChangelog = async ({
40+
shas = true,
41+
authors = true,
42+
previousTag = true,
43+
commits: rawCommits = [{
4144
sha: 'a',
4245
type: 'feat',
43-
notes: [],
4446
bareMessage: 'Hey now',
4547
scope: 'bin',
4648
}, {
@@ -55,13 +57,15 @@ const mockChangelog = async ({ shas = true, authors = true, previousTag = true }
5557
sha: 'c',
5658
type: 'deps',
5759
bareMessage: '[email protected]',
58-
notes: [],
5960
}, {
6061
sha: 'd',
6162
type: 'fix',
6263
bareMessage: 'this fixes it',
63-
notes: [],
64-
}].map(({ sha, ...rest }) => shas ? { sha, ...rest } : rest)
64+
}],
65+
} = {}) => {
66+
const commits = rawCommits
67+
.map(({ notes = [], ...rest }) => ({ notes, ...rest }))
68+
.map(({ sha, ...rest }) => shas ? { sha, ...rest } : { ...rest })
6569

6670
const github = mockGitHub({ commits, authors })
6771
const changelog = new ChangelogNotes(github)
@@ -112,3 +116,46 @@ t.test('no tag/authors/shas', async t => {
112116
113117
])
114118
})
119+
120+
t.test('filters out multiple template oss commits', async t => {
121+
const changelog = await mockChangelog({
122+
authors: false,
123+
commits: [{
124+
sha: 'a',
125+
type: 'chore',
126+
bareMessage: 'postinstall for dependabot template-oss PR',
127+
pullRequest: {
128+
number: '100',
129+
},
130+
}, {
131+
sha: 'b',
132+
type: 'chore',
133+
bareMessage: 'postinstall for dependabot template-oss PR',
134+
pullRequest: {
135+
number: '101',
136+
},
137+
}, {
138+
sha: 'c',
139+
type: 'chore',
140+
bareMessage: 'bump @npmcli/template-oss from 1 to 2',
141+
pullRequest: {
142+
number: '101',
143+
},
144+
}, {
145+
sha: 'd',
146+
type: 'chore',
147+
bareMessage: 'bump @npmcli/template-oss from 0 to 1',
148+
pullRequest: {
149+
number: '100',
150+
},
151+
}],
152+
})
153+
t.strictSame(changelog, [
154+
'## [1.0.0](https://github.com/npm/cli/compare/v0.1.0...v1.0.0) (DATE)',
155+
'### Chores',
156+
// eslint-disable-next-line max-len
157+
'* [`b`](https://github.com/npm/cli/commit/b) [#101](https://github.com/npm/cli/pull/101) postinstall for dependabot template-oss PR',
158+
// eslint-disable-next-line max-len
159+
'* [`c`](https://github.com/npm/cli/commit/c) [#101](https://github.com/npm/cli/pull/101) bump @npmcli/template-oss from 1 to 2',
160+
])
161+
})

0 commit comments

Comments
 (0)