Skip to content

Commit 4f82c83

Browse files
committed
chore: Refactor generate_changelog script.
1 parent fc920bd commit 4f82c83

File tree

2 files changed

+79
-85
lines changed

2 files changed

+79
-85
lines changed

scripts/create_release.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ async function main() {
8181

8282
console.log("Creating release...");
8383
const changelog = await exec(
84-
`node ${join(__dirname, "generate_changelog.js")} ${lastTag} - github`
84+
`node ${join(__dirname, "generate_changelog.js")} ${lastTag} -`
8585
);
8686
await createGitHubRelease({
8787
tag_name: currentVersion,

scripts/generate_changelog.js

Lines changed: 78 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const assert = require("assert");
1515

1616
// Commits older than this didn't follow conventional commits, so there's no
1717
// easy way to guess how to categorize them.
18-
const OLDEST_CHANGELOG_COMMIT = "f388b42d1a16656681bedfc45f33f1d856441c58";
18+
const OLDEST_VERSION = "v0.16.5";
1919

2020
const categories = [
2121
{ prefix: "BREAKING CHANGE", category: "Breaking Changes" },
@@ -25,11 +25,49 @@ const categories = [
2525

2626
const maintainers = new Set(["Gerrit Birkeland", "dependabot[bot]"]);
2727

28-
/** @param {string} since */
29-
async function getLogs(since) {
30-
const { stdout: log } = await promisify(exec)(
31-
`git log --pretty="format:%H%x00%at%x00%an%x00%B%x00" --no-merges ${since}..master`,
32-
{ encoding: "utf-8" }
28+
/** @param {string} command */
29+
async function run(command) {
30+
const result = await promisify(exec)(command, {
31+
encoding: "utf-8",
32+
});
33+
return result.stdout;
34+
}
35+
36+
async function getVersions() {
37+
const VERSION_REGEX = /^v(\d+)\.(\d+)\.(\d+)$/;
38+
39+
const tags = await run("git tag -l");
40+
const versionTags = tags.split("\n").filter((t) => VERSION_REGEX.test(t));
41+
42+
versionTags.sort((a, b) => {
43+
const [a1, a2, a3] = a
44+
.match(VERSION_REGEX)
45+
.slice(1)
46+
.map((v) => parseInt(v));
47+
const [b1, b2, b3] = b
48+
.match(VERSION_REGEX)
49+
.slice(1)
50+
.map((v) => parseInt(v));
51+
52+
if (a1 == b1) {
53+
if (a2 == b2) {
54+
return b3 - a3;
55+
}
56+
return b2 - a2;
57+
}
58+
return b1 - a1;
59+
});
60+
61+
return versionTags.slice(0, versionTags.indexOf(OLDEST_VERSION));
62+
}
63+
64+
/**
65+
* @param {string} previous
66+
* @param {string} version
67+
*/
68+
async function getLogs(previous, version) {
69+
const log = await run(
70+
`git log --pretty="format:%H%x00%at%x00%an%x00%B%x00" --no-merges ${previous}..${version}`
3371
);
3472

3573
/** @type {LogEntry[]} */
@@ -59,18 +97,13 @@ async function getLogs(since) {
5997
/**
6098
* @param {string} version
6199
* @param {Date} date
62-
* @param {string} target
63100
*/
64-
function getHeader(version, date, target) {
65-
// In github mode, we're only generating the changelog for a single release.
66-
if (target === "github") return;
67-
101+
function getHeader(version, date) {
68102
version = version.trim();
69103

70-
const dateString = `(${date.getFullYear()}-${date
71-
.getUTCMonth()
72-
.toString()
73-
.padStart(2, "0")}-${date.getUTCDate().toString().padStart(2, "0")})`;
104+
const month = date.getUTCMonth().toString().padStart(2, "0");
105+
const day = date.getUTCDate().toString().padStart(2, "0");
106+
const dateString = ` (${date.getFullYear()}-${month}-${day})`;
74107

75108
if (/^\d+\.\d+\.\d+$/.test(version)) {
76109
const [_major, minor, patch] = version.split(".");
@@ -85,44 +118,6 @@ function getHeader(version, date, target) {
85118
return `# ${version}${dateString}`;
86119
}
87120

88-
/**
89-
* @param {readonly LogEntry[]} entries
90-
* @param {string} target
91-
*/
92-
function groupByVersions(entries, target) {
93-
/** @type {[string, LogEntry[]][]} */
94-
const result = [];
95-
96-
let header = "# Unreleased";
97-
/** @type {LogEntry[]} */
98-
let current = [];
99-
100-
for (const entry of entries) {
101-
const match = entry.message.match(/^chore: Bump version to (.*)/m);
102-
if (match) {
103-
// Beta version bump
104-
if (match[1].includes("-")) {
105-
continue;
106-
}
107-
108-
if (current.length > 0) {
109-
result.push([header, current]);
110-
}
111-
header = getHeader(match[1], entry.date, target);
112-
current = [];
113-
continue;
114-
}
115-
116-
current.push(entry);
117-
}
118-
119-
if (current.length > 0) {
120-
result.push([header, current]);
121-
}
122-
123-
return result;
124-
}
125-
126121
/**
127122
* @param {RegExp} regex
128123
* @param {string} str
@@ -161,13 +156,8 @@ function getThanks(commits) {
161156

162157
/**
163158
* @param {string} commit
164-
* @param {string} target
165159
*/
166-
function commitLink(commit, target) {
167-
if (target === "github") {
168-
return commit;
169-
}
170-
160+
function commitLink(commit) {
171161
return `[${commit.substr(
172162
0,
173163
7
@@ -176,22 +166,17 @@ function commitLink(commit, target) {
176166

177167
/**
178168
* @param {string} message
179-
* @param {string} target
180169
*/
181-
function issueLinks(message, target) {
170+
function issueLinks(message) {
182171
/** @type {Set<string>} */
183172
const issues = new Set();
184173

185174
for (const match of matchAll(
186175
/(close|resolve|fixe?)[sd]? #(\d+)/gi,
187176
message
188177
)) {
189-
if (target == "github") {
190-
issues.add(`#${match[2]}`);
191-
} else {
192-
const url = `https://github.com/TypeStrong/typedoc/issues/${match[2]}`;
193-
issues.add(`[#${match[2]}](${url})`);
194-
}
178+
const url = `https://github.com/TypeStrong/typedoc/issues/${match[2]}`;
179+
issues.add(`[#${match[2]}](${url})`);
195180
}
196181

197182
if (issues.size === 0) {
@@ -202,9 +187,8 @@ function issueLinks(message, target) {
202187

203188
/**
204189
* @param {readonly LogEntry[]} commits
205-
* @param {string} target
206190
*/
207-
function getCategories(commits, target) {
191+
function getCategories(commits) {
208192
/** @type {Map<string, string>} */
209193
const result = new Map();
210194

@@ -219,8 +203,8 @@ function getCategories(commits, target) {
219203
[
220204
" * ",
221205
line.replace(/^[^:]+:\s*/, ""),
222-
` (${commitLink(commit.commit, target)})`,
223-
issueLinks(commit.message, target),
206+
` (${commitLink(commit.commit)})`,
207+
issueLinks(commit.message),
224208
].join("")
225209
);
226210
}
@@ -237,13 +221,12 @@ function getCategories(commits, target) {
237221

238222
/**
239223
* @param {readonly LogEntry[]} commits
240-
* @param {string} target
241224
*/
242-
function getBody(commits, target) {
225+
function getBody(commits) {
243226
/** @type {string[]} */
244227
const lines = [];
245228

246-
for (const [name, body] of getCategories(commits, target)) {
229+
for (const [name, body] of getCategories(commits)) {
247230
lines.push(`### ${name}`, "", body, "");
248231
}
249232

@@ -256,22 +239,33 @@ function getBody(commits, target) {
256239
return lines.join("\n");
257240
}
258241

259-
async function main(
260-
since = OLDEST_CHANGELOG_COMMIT,
261-
where = "CHANGELOG.md",
262-
target = "standalone" // standalone | github
263-
) {
264-
const entries = await getLogs(since);
242+
async function main(fromVersion = OLDEST_VERSION, where = "CHANGELOG.md") {
243+
const versions = await getVersions();
244+
let end = versions.indexOf(fromVersion) + 1;
245+
if (end === 0) {
246+
if (fromVersion !== OLDEST_VERSION) {
247+
throw new Error("Invalid version");
248+
}
249+
end = versions.length;
250+
}
265251

266252
/** @type {string[]} */
267253
const out = [];
268254

269-
for (const [header, commits] of groupByVersions(entries, target)) {
270-
if (header) {
271-
out.push(header, "");
255+
for (let i = 0; i < end; i++) {
256+
const logs = await getLogs(
257+
i + 1 === versions.length ? OLDEST_VERSION : versions[i + 1],
258+
versions[i]
259+
);
260+
assert(logs.length > 0, `${versions[i]} has no commit logs`);
261+
262+
if (end !== 1) {
263+
out.push(getHeader(versions[i], logs[0].date), "");
272264
}
273-
out.push(getBody(commits, target), "", "");
265+
266+
out.push(getBody(logs), "");
274267
}
268+
275269
out.pop();
276270

277271
if (where == "-") {

0 commit comments

Comments
 (0)