| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -33,30 +33,43 @@ jobs: | |||
| 33 | 33 | echo "COMMIT_SUBJECT=$COMMIT_SUBJECT" >> "$GITHUB_ENV" | |
| 34 | 34 | - name: Lint release commit message trailers | |
| 35 | 35 | run: | | |
| 36 | - EXPECTED_TRAILER="^PR-URL: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/pull/[[:digit:]]+\$" | ||
| 36 | + EXPECTED_TRAILER="^$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/pull/[[:digit:]]+\$" | ||
| 37 | 37 | 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)')" | ||
| 39 | 39 | echo "Actual: $ACTUAL" | |
| 40 | - echo "$ACTUAL" | grep -E -q "$EXPECTED_TRAILER" | ||
| 40 | + echo "$PR_URL" | grep -E -q "$EXPECTED_TRAILER" | ||
| 41 | 41 | ||
| 42 | - PR_URL="${ACTUAL:8}" | ||
| 43 | 42 | PR_HEAD="$(gh pr view "$PR_URL" --json headRefOid -q .headRefOid)" | |
| 44 | 43 | echo "Head of $PR_URL: $PR_HEAD" | |
| 45 | 44 | echo "Current commit: $GITHUB_SHA" | |
| 46 | 45 | [ "$PR_HEAD" = "$GITHUB_SHA" ] | |
| 47 | 46 | env: | |
| 48 | 47 | GH_TOKEN: ${{ github.token }} | |
| 48 | + - name: Verify it's release-ready | ||
| 49 | + run: | | ||
| 50 | + SKIP_XZ=1 make release-only | ||
| 49 | 51 | - name: Validate CHANGELOG | |
| 50 | 52 | id: releaser-info | |
| 51 | 53 | run: | | |
| 52 | 54 | EXPECTED_CHANGELOG_TITLE_INTRO="## $COMMIT_SUBJECT, @" | |
| 53 | 55 | 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")" | ||
| 55 | 59 | echo "Actual: $CHANGELOG_TITLE" | |
| 56 | 60 | [ "${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 }} | ||
| Original file line number | Diff line number | Diff 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'); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments