Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4ff9aa7

Browse filesBrowse files
aduh95marco-ippolito
authored andcommitted
tools: validate commit list as part of lint-release-commit
PR-URL: #56291 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent c06ac66 commit 4ff9aa7
Copy full SHA for 4ff9aa7

File tree

Expand file treeCollapse file tree

2 files changed

+86
-11
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+86
-11
lines changed

‎.github/workflows/lint-release-proposal.yml

Copy file name to clipboardExpand all lines: .github/workflows/lint-release-proposal.yml
+24-11Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,43 @@ jobs:
3333
echo "COMMIT_SUBJECT=$COMMIT_SUBJECT" >> "$GITHUB_ENV"
3434
- name: Lint release commit message trailers
3535
run: |
36-
EXPECTED_TRAILER="^PR-URL: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/pull/[[:digit:]]+\$"
36+
EXPECTED_TRAILER="^$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/pull/[[:digit:]]+\$"
3737
echo "Expected trailer format: $EXPECTED_TRAILER"
38-
ACTUAL="$(git --no-pager log -1 --format=%b | git interpret-trailers --parse --no-divider)"
38+
PR_URL="$(git --no-pager log -1 --format='%(trailers:key=PR-URL,valueonly)')"
3939
echo "Actual: $ACTUAL"
40-
echo "$ACTUAL" | grep -E -q "$EXPECTED_TRAILER"
40+
echo "$PR_URL" | grep -E -q "$EXPECTED_TRAILER"
4141
42-
PR_URL="${ACTUAL:8}"
4342
PR_HEAD="$(gh pr view "$PR_URL" --json headRefOid -q .headRefOid)"
4443
echo "Head of $PR_URL: $PR_HEAD"
4544
echo "Current commit: $GITHUB_SHA"
4645
[ "$PR_HEAD" = "$GITHUB_SHA" ]
4746
env:
4847
GH_TOKEN: ${{ github.token }}
48+
- name: Verify it's release-ready
49+
run: |
50+
SKIP_XZ=1 make release-only
4951
- name: Validate CHANGELOG
5052
id: releaser-info
5153
run: |
5254
EXPECTED_CHANGELOG_TITLE_INTRO="## $COMMIT_SUBJECT, @"
5355
echo "Expected CHANGELOG section title: $EXPECTED_CHANGELOG_TITLE_INTRO"
54-
CHANGELOG_TITLE="$(grep "$EXPECTED_CHANGELOG_TITLE_INTRO" "doc/changelogs/CHANGELOG_V${COMMIT_SUBJECT:20:2}.md")"
56+
MAJOR="$(awk '/^#define NODE_MAJOR_VERSION / { print $3 }' src/node_version.h)"
57+
CHANGELOG_PATH="doc/changelogs/CHANGELOG_V${MAJOR}.md"
58+
CHANGELOG_TITLE="$(grep "$EXPECTED_CHANGELOG_TITLE_INTRO" "$CHANGELOG_PATH")"
5559
echo "Actual: $CHANGELOG_TITLE"
5660
[ "${CHANGELOG_TITLE%%@*}@" = "$EXPECTED_CHANGELOG_TITLE_INTRO" ]
57-
- name: Verify NODE_VERSION_IS_RELEASE bit is correctly set
58-
run: |
59-
grep -q '^#define NODE_VERSION_IS_RELEASE 1$' src/node_version.h
60-
- name: Check for placeholders in documentation
61-
run: |
62-
! grep "REPLACEME" doc/api/*.md
61+
gh api \
62+
-H "Accept: application/vnd.github+json" \
63+
-H "X-GitHub-Api-Version: 2022-11-28" \
64+
--jq '.commits.[] | { smallSha: .sha[0:10] } + (.commit.message|capture("^(?<title>.+)\n\n(.*\n)*PR-URL: (?<prURL>.+)\n"))' \
65+
"/repos/${GITHUB_REPOSITORY}/compare/v${MAJOR}.x...$GITHUB_SHA" --paginate \
66+
| node tools/actions/lint-release-proposal-commit-list.mjs "$CHANGELOG_PATH" "$GITHUB_SHA" \
67+
| while IFS= read -r PR_URL; do
68+
LABEL="dont-land-on-v${MAJOR}.x" gh pr view \
69+
--json labels,url \
70+
--jq 'if (.labels|map(.name==env.LABEL)|any) then error("\(.url) has the \(env.LABEL) label, forbidding it to be in this release proposal") end' \
71+
"$PR_URL" > /dev/null
72+
done
73+
shell: bash # See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference, we want the pipefail option.
74+
env:
75+
GH_TOKEN: ${{ github.token }}
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env node
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+
// Example:
7+
// $ git log upstream/vXX.x...upstream/vX.X.X-proposal \
8+
// --format='{"prURL":"%(trailers:key=PR-URL,valueonly,separator=)","title":"%s","smallSha":"%h"}' \
9+
// | ./lint-release-proposal-commit-list.mjs "path/to/CHANGELOG.md" "$(git rev-parse upstream/vX.X.X-proposal)"
10+
11+
const [,, CHANGELOG_PATH, RELEASE_COMMIT_SHA] = process.argv;
12+
13+
import assert from 'node:assert';
14+
import { readFile } from 'node:fs/promises';
15+
import { createInterface } from 'node:readline';
16+
17+
// Creating the iterator early to avoid missing any data:
18+
const stdinLineByLine = createInterface(process.stdin)[Symbol.asyncIterator]();
19+
20+
const changelog = await readFile(CHANGELOG_PATH, 'utf-8');
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)
24+
// Checking for semverness is too expansive, it is left as a exercice for human reviewers.
25+
.replaceAll('**(SEMVER-MINOR)** ', '')
26+
// Correct Markdown escaping is validated by the linter, getting rid of it here helps.
27+
.replaceAll('\\', '');
28+
29+
let expectedNumberOfCommitsLeft = commitList.match(/\n\* \[/g).length;
30+
for await (const line of stdinLineByLine) {
31+
const { smallSha, title, prURL } = JSON.parse(line);
32+
33+
if (smallSha === RELEASE_COMMIT_SHA.slice(0, 10)) {
34+
assert.strictEqual(
35+
expectedNumberOfCommitsLeft, 0,
36+
'Some commits are listed without being included in the proposal, or are listed more than once',
37+
);
38+
continue;
39+
}
40+
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 colonIndex = title.indexOf(':');
46+
const expectedCommitTitle = `${`**${title.slice(0, colonIndex)}`.replace('**Revert "', '_**Revert**_ "**')}**${title.slice(colonIndex)}`;
47+
try {
48+
assert(commitList.lastIndexOf(`/${smallSha})] - ${expectedCommitTitle} (`, lineEnd) > lineStart, 'Commit title doesn\'t match');
49+
} catch (e) {
50+
if (e?.code === 'ERR_ASSERTION') {
51+
e.operator = 'includes';
52+
e.expected = expectedCommitTitle;
53+
e.actual = commitList.slice(lineStart + 1, lineEnd);
54+
}
55+
throw e;
56+
}
57+
assert.strictEqual(commitList.slice(lineEnd - prURL.length - 2, lineEnd), `(${prURL})`, `when checking ${smallSha} ${title}`);
58+
59+
expectedNumberOfCommitsLeft--;
60+
console.log(prURL);
61+
}
62+
assert.strictEqual(expectedNumberOfCommitsLeft, 0, 'Release commit is not the last commit in the proposal');

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.