|
1 | 1 | #!/usr/bin/env node
|
2 | 2 |
|
| 3 | +// Takes a stream of JSON objects as inputs, validates the CHANGELOG contains a |
| 4 | +// line corresponding, then outputs the prURL value. |
| 5 | +// |
| 6 | +// $ ./lint-release-proposal-commit-list.mjs "path/to/CHANGELOG.md" "deadbeef00" <<'EOF' |
| 7 | +// {"prURL":"https://github.com/nodejs/node/pull/56131","smallSha":"d48b5224c0","splitTitle":["doc"," fix module.md headings"]} |
| 8 | +// {"prURL":"https://github.com/nodejs/node/pull/56123","smallSha":"f1c2d2f65e","splitTitle":["doc"," update blog release-post link"]} |
| 9 | +// EOF |
| 10 | + |
3 | 11 | const [,, CHANGELOG_PATH, RELEASE_COMMIT_SHA] = process.argv;
|
4 | 12 |
|
5 | 13 | import assert from 'node:assert';
|
6 | 14 | import { readFile } from 'node:fs/promises';
|
7 | 15 | import { createInterface } from 'node:readline';
|
8 | 16 |
|
9 |
| -// Creating the iterator early to avoid missing any data |
| 17 | +// Creating the iterator early to avoid missing any data: |
10 | 18 | const stdinLineByLine = createInterface(process.stdin)[Symbol.asyncIterator]();
|
11 | 19 |
|
12 | 20 | const changelog = await readFile(CHANGELOG_PATH, 'utf-8');
|
13 |
| -const startCommitListing = changelog.indexOf('\n### Commits\n'); |
14 |
| -const commitList = changelog.slice(startCommitListing, changelog.indexOf('\n\n<a', startCommitListing)) |
| 21 | +const commitListingStart = changelog.indexOf('\n### Commits\n'); |
| 22 | +const commitListingEnd = changelog.indexOf('\n\n<a', commitListingStart); |
| 23 | +const commitList = changelog.slice(commitListingStart, commitListingEnd === -1 ? undefined : commitListingEnd + 1) |
15 | 24 | // Checking for semverness is too expansive, it is left as a exercice for human reviewers.
|
16 | 25 | .replaceAll('**(SEMVER-MINOR)** ', '')
|
17 | 26 | // Correct Markdown escaping is validated by the linter, getting rid of it here helps.
|
18 | 27 | .replaceAll('\\', '');
|
19 | 28 |
|
20 | 29 | let expectedNumberOfCommitsLeft = commitList.match(/\n\* \[/g).length;
|
21 | 30 | for await (const line of stdinLineByLine) {
|
22 |
| - if (line.includes(RELEASE_COMMIT_SHA.slice(0, 10))) { |
| 31 | + const { smallSha, splitTitle, prURL } = JSON.parse(line); |
| 32 | + |
| 33 | + if (smallSha === RELEASE_COMMIT_SHA.slice(0, 10)) { |
23 | 34 | assert.strictEqual(
|
24 | 35 | expectedNumberOfCommitsLeft, 0,
|
25 | 36 | 'Some commits are listed without being included in the proposal, or are listed more than once',
|
26 | 37 | );
|
27 | 38 | continue;
|
28 | 39 | }
|
29 | 40 |
|
30 |
| - // Revert commit have a special treatment. |
31 |
| - const fixedLine = line.replace(' - **Revert \"', ' - _**Revert**_ "**'); |
| 41 | + const lineStart = commitList.indexOf(`\n* \[[\`${smallSha}\`]`); |
| 42 | + assert.notStrictEqual(lineStart, -1, `Cannot find ${smallSha} on the list`); |
| 43 | + const lineEnd = commitList.indexOf('\n', lineStart + 1); |
| 44 | + |
| 45 | + const expectedCommitTitle = `${`**${splitTitle.shift()}`.replace('**Revert \"', '_**Revert**_ "**')}**:${splitTitle.join(':')}`; |
| 46 | + try { |
| 47 | + assert(commitList.lastIndexOf(`/${smallSha})] - ${expectedCommitTitle} (`, lineEnd) > lineStart, `Commit title doesn't match`); |
| 48 | + } catch (e) { |
| 49 | + if (e?.code === 'ERR_ASSERTION') { |
| 50 | + e.operator = 'includes'; |
| 51 | + e.expected = expectedCommitTitle; |
| 52 | + e.actual = commitList.slice(lineStart + 1, lineEnd); |
| 53 | + } |
| 54 | + throw e; |
| 55 | + } |
| 56 | + assert.strictEqual(commitList.slice(lineEnd - prURL.length - 2, lineEnd), `(${prURL})`); |
32 | 57 |
|
33 |
| - assert(commitList.includes('\n' + fixedLine), `Missing "${fixedLine}" in commit list`); |
34 | 58 | expectedNumberOfCommitsLeft--;
|
| 59 | + console.log(prURL); |
35 | 60 | }
|
36 | 61 | assert.strictEqual(expectedNumberOfCommitsLeft, 0, 'Release commit is not the last commit in the proposal');
|
0 commit comments