#!/usr/bin/env node // Takes a stream of JSON objects as inputs, validates the CHANGELOG contains a // line corresponding, then outputs the prURL value. // // Example: // $ git log upstream/vXX.x...upstream/vX.X.X-proposal \ // --format='{"prURL":"%(trailers:key=PR-URL,valueonly,separator=)","title":"%s","smallSha":"%h"}' \ // | ./lint-release-proposal-commit-list.mjs "path/to/CHANGELOG.md" "$(git rev-parse upstream/vX.X.X-proposal)" const [,, CHANGELOG_PATH, RELEASE_COMMIT_SHA] = process.argv; import assert from 'node:assert'; import { readFile } from 'node:fs/promises'; import { createInterface } from 'node:readline'; // Creating the iterator early to avoid missing any data: const stdinLineByLine = createInterface(process.stdin)[Symbol.asyncIterator](); const changelog = await readFile(CHANGELOG_PATH, 'utf-8'); const commitListingStart = changelog.indexOf('\n### Commits\n'); let commitList; if (commitListingStart === -1) { // We're preparing a semver-major release. commitList = changelog.replace(/(^.+\n### Semver-Major|\n### Semver-(Minor|Patch)) Commits\n/gs, '') .replaceAll('**(SEMVER-MAJOR)** ', ''); } else { const commitListingEnd = changelog.indexOf('\n\n lineStart, `Commit title doesn't match`); } catch (e) { if (e?.code === 'ERR_ASSERTION') { e.operator = 'includes'; e.expected = expectedCommitTitle; e.actual = commitList.slice(lineStart + 1, lineEnd); } throw e; } assert.strictEqual(commitList.slice(lineEnd - prURL.length - 2, lineEnd), `(${prURL})`, `when checking ${smallSha} ${title}`); expectedNumberOfCommitsLeft--; console.log(prURL); } assert.strictEqual(expectedNumberOfCommitsLeft, 0, 'Release commit is not the last commit in the proposal');