Skip to content

Commit 5b16f85

Browse files
authored
release: add release log generation script (#54006)
Add script of generating release log. To access the changelog just run the below script (copy content with `pbcopy` on MacOS) ```js node ./scripts/generate-release-log.mjs | pbcopy ```
1 parent c4f9c6d commit 5b16f85

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

scripts/generate-release-log.mjs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import fetch from 'node-fetch'
2+
3+
async function main() {
4+
const releasesArray = await fetch(
5+
'https://api.github.com/repos/vercel/next.js/releases?per_page=100'
6+
).then((r) => r.json())
7+
8+
const allReleases = releasesArray
9+
.map(({ id, tag_name, created_at, body }) => ({
10+
id,
11+
tag_name,
12+
created_at,
13+
body: body
14+
.replace(/\r\n/g, '\n')
15+
.split('\n')
16+
.map((e) => e.trim()),
17+
}))
18+
.sort((a, b) => a.created_at.localeCompare(b.created_at))
19+
20+
// targetVersion format is `13.4.15-`, generating changes for all 13.4.15-* canary releases
21+
const targetVersion = /v(.*?-)/
22+
.exec(allReleases.filter((e) => /v.*?-/.exec(e.tag_name)).pop().tag_name)
23+
.pop()
24+
25+
const releases = allReleases.filter((v) => v.tag_name.includes(targetVersion))
26+
27+
const lineItems = {
28+
'### Core Changes': [],
29+
'### Minor Changes': [],
30+
'### Documentation Changes': [],
31+
'### Example Changes': [],
32+
'### Misc Changes': [],
33+
'### Patches': [],
34+
'### Credits': [],
35+
}
36+
37+
Object.keys(lineItems).forEach((header) => {
38+
releases.forEach((release) => {
39+
const headerIndex = release.body.indexOf(header)
40+
41+
if (!~headerIndex) return
42+
43+
let headerLastIndex = release.body
44+
.slice(headerIndex + 1)
45+
.findIndex((v) => v.startsWith('###'))
46+
47+
if (~headerLastIndex) {
48+
headerLastIndex = headerLastIndex + headerIndex
49+
} else {
50+
headerLastIndex = release.body.length - 1
51+
}
52+
53+
if (header === '### Credits') {
54+
release.body.slice(headerIndex, headerLastIndex + 1).forEach((e) => {
55+
const re = /@[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}/gi
56+
let m
57+
58+
do {
59+
m = re.exec(e)
60+
61+
if (m) {
62+
lineItems[header].push(m.pop())
63+
}
64+
} while (m)
65+
})
66+
} else {
67+
release.body.slice(headerIndex, headerLastIndex + 1).forEach((e) => {
68+
if (!e.startsWith('-')) {
69+
return
70+
}
71+
lineItems[header].push(e)
72+
})
73+
}
74+
})
75+
})
76+
77+
let finalMessage = []
78+
79+
Object.keys(lineItems).forEach((header) => {
80+
let items = lineItems[header]
81+
82+
if (!items.length) {
83+
return
84+
}
85+
finalMessage.push(header)
86+
finalMessage.push('')
87+
88+
if (header === '### Credits') {
89+
items = [...new Set(items)]
90+
let creditsMessage = `Huge thanks to `
91+
92+
if (items.length > 1) {
93+
creditsMessage += items.slice(0, items.length - 1).join(`, `)
94+
creditsMessage += `, and `
95+
}
96+
creditsMessage += items[items.length - 1]
97+
creditsMessage += ` for helping!`
98+
99+
finalMessage.push(creditsMessage)
100+
} else {
101+
items.forEach((e) => finalMessage.push(e))
102+
}
103+
104+
finalMessage.push('')
105+
})
106+
107+
return {
108+
version: targetVersion.slice(0, -1),
109+
firstVersion: releases[0].tag_name,
110+
lastVersion: releases[releases.length - 1].tag_name,
111+
content: finalMessage.join('\n'),
112+
}
113+
}
114+
115+
main().then((result) => {
116+
console.log(result.content)
117+
})

0 commit comments

Comments
 (0)