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 5f6785b

Browse filesBrowse files
committed
tools: validate commit list as part of lint-release-commit
PR-URL: #56291 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 00a1943 commit 5f6785b
Copy full SHA for 5f6785b

File tree

Expand file treeCollapse file tree

2 files changed

+86
-11
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

2 files changed

+86
-11
lines changed
Open diff view settings
Collapse file

‎.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 }}
Collapse file
+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.