forked from nodejs/node-api-headers
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdate-changelog.js
141 lines (115 loc) · 3.99 KB
/
update-changelog.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
const { exec, spawn } = require("node:child_process");
const { createReadStream } = require("node:fs");
const { createInterface } = require("node:readline");
const { resolve: resolvePath } = require("node:path");
const { writeFile } = require("node:fs/promises");
/**
* Returns a string of the new changelog entries by running `npx changelog-maker
* --format=markdown`.
*
* @returns {Promise<string>}
*/
async function getNewChangelogEntries() {
const { stdout, stderr } = await new Promise((resolve, reject) => {
// Echo an empty string to pass as the GitHub Personal Access Token
// (PAT). This causes the process to error if no PAT is found in the
// changelog-maker configuration file.
exec("echo '' | npx changelog-maker --format=markdown", (err, stdout, stderr) => {
if (err) {
reject(err);
} else {
resolve({ stdout, stderr });
}
});
});
return { stdout, stderr };
}
/**
* Returns the text of the changelog file, excluding header lines.
*
* @param {string} changelogPath Path to changelog file
* @returns {Promise<string>}
*/
async function getExistingChangelogText(changelogPath) {
const data = await new Promise((resolve, reject) => {
try {
const rl = createInterface(createReadStream(changelogPath));
let lines = "";
let lineNumber = 1;
rl.on('line', function (line) {
if (lineNumber > 2) {
lines += line + "\n";
}
lineNumber++;
});
rl.on('close', () => {
resolve(lines);
});
rl.on('error', (err) => {
reject(err);
});
} catch (e) {
reject(e);
}
});
return data;
}
/**
* Returns the string for the new changelog file.
*
* @param {string} newEntries New changelog entries
* @param {string} existingText Existing changelog text
* @param {string} author Author of the release
* @returns {string}
*/
function generateChangelogText(newEntries, existingText, author = "github-actions\\[bot]") {
const packageVersion = require("../package.json").version;
const currentDateString = new Date().toISOString().split(/T/)[0];
const notableChanges = Array.from(newEntries.matchAll(/ (- [^(]+) \([^)]+\)( \[#\d+]\([^)]+\))?/g))
.map(matches => matches[1])
.join("\n");
return `# node-api-headers Changelog
## ${currentDateString} Version ${packageVersion}, ${author}
### Notable changes
${notableChanges}
### Commits
${newEntries.trim()}
${existingText.trim()}
`;
}
/**
* Throws an error (asynchronously) if there are uncommitted changes to the changelog file.
*
* @param {string} changelogPath Path to changelog file
* @returns {Promise<void>}
*/
function assertCleanChangelog(changelogPath) {
return new Promise((resolve, reject) => {
const spawned = spawn("git", ["diff", "--quiet", changelogPath]);
spawned.on('exit', function (exitCode) {
if (exitCode === 0) {
resolve(undefined);
} else {
reject(new Error(`There are uncommitted changes to ${changelogPath}. Commit, revert, or stash changes first.`));
}
});
spawned.on('error', function (err) {
reject(err);
});
});
}
async function main() {
const changelogPath = resolvePath(__dirname, "..", "CHANGELOG.md");
await assertCleanChangelog(changelogPath);
const [{ stdout: newEntires, stderr }, existingText] = await Promise.all([getNewChangelogEntries(), getExistingChangelogText(changelogPath)]);
const changelogText = generateChangelogText(newEntires, existingText);
await writeFile(changelogPath, changelogText);
if (stderr) {
console.error("stderr from changelog-maker:\n", stderr)
}
console.log(`Changelog written to ${changelogPath}`);
}
main().catch(e => {
console.error(e);
process.exitCode = 1;
});