Files
node/.github/workflows/lint-release-proposal.yml
T
Rafael Gonzaga 7abbd56cfd doc: document the alpha release process
Document how to prepare, version, tag and promote the Alpha releases
introduced by the new release schedule, starting with Node.js 27, and
how the first Alpha and semver-major releases are prepared with
git node release.

Refs: https://github.com/nodejs/Release/issues/1187
Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com>
PR-URL: https://github.com/nodejs/node/pull/66796
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Richard Lau <richard.lau@ibm.com>
2026-10-26 16:32:08 +00:00

124 lines
5.5 KiB
YAML

name: Linters (release proposals)
on:
push:
branches:
- v[0-9]+.[0-9]+.[0-9]+-proposal
- v[0-9]+.0.0-alpha.[0-9]+.[0-9]+.[0-9]+-proposal
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
PYTHON_VERSION: '3.15'
NODE_VERSION: lts/*
permissions:
contents: read
jobs:
lint-release-commit:
runs-on: ubuntu-slim
permissions:
contents: read
pull-requests: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 2
- name: Lint release commit title format
id: commit-message-parse
run: |
VERSION=$(${CXX:-cc} -E -dM src/node_version.h | awk '
$2 == "NODE_MAJOR_VERSION" { maj = $3 }
$2 == "NODE_MINOR_VERSION" { min = $3 }
$2 == "NODE_PATCH_VERSION" { pat = $3 }
$2 == "NODE_ALPHA_MAJOR_VERSION" { amaj = $3 }
$2 == "NODE_ALPHA_MINOR_VERSION" { amin = $3 }
$2 == "NODE_ALPHA_PATCH_VERSION" { apat = $3 }
$2 == "NODE_VERSION_LTS_CODENAME" { gsub(/^"|"$/, "'"'"'",$3); lts = $3 }
END { if (maj) print maj "\\." min "\\." pat (amaj != "" ? "-alpha\\." amaj "\\." amin "\\." apat " \\(Alpha" : " " (lts != "'"''"'" ? lts " \\(LTS" : "\\(Current" )) "\\)" }
')
MAJOR=${VERSION%%\\.*}
EXPECTED_TITLE='^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}, Version '"$VERSION"'$'
echo "Expected commit title format: $EXPECTED_TITLE"
COMMIT_SUBJECT="$(git --no-pager log -1 --format=%s)"
echo "Actual: $COMMIT_SUBJECT"
echo "$COMMIT_SUBJECT" | grep -q -E "$EXPECTED_TITLE"
echo "COMMIT_SUBJECT=$COMMIT_SUBJECT" >> "$GITHUB_OUTPUT"
echo "MAJOR=$MAJOR" >> "$GITHUB_OUTPUT"
- name: Lint release commit message trailers
run: |
EXPECTED_TRAILER="^$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/pull/[[:digit:]]+\$"
echo "Expected trailer format: $EXPECTED_TRAILER"
PR_URL="$(git --no-pager log -1 --format='%(trailers:key=PR-URL,valueonly)')"
echo "Actual: $PR_URL"
echo "$PR_URL" | grep -E -q "$EXPECTED_TRAILER"
PR_HEAD="$(gh pr view "$PR_URL" --json headRefOid -q .headRefOid)"
echo "Head of $PR_URL: $PR_HEAD"
echo "Current commit: $GITHUB_SHA"
[ "$PR_HEAD" = "$GITHUB_SHA" ]
env:
GH_TOKEN: ${{ github.token }}
- name: Verify it's release-ready
run: |
SKIP_XZ=1 make release-only
- name: Lint release commit content
run: |
echo "Checking for expected files in the release commit:"
missing_expected=
for expected in CHANGELOG.md src/node_version.h doc/changelogs/; do
if git diff --exit-code --quiet --diff-filter=M HEAD^ -- "$expected"; then
echo "Missing expected file in diff: $expected"
missing_expected=1
fi
done
[ -z "$missing_expected" ] || exit 1
echo "Checking for unexpected files in the release commit:"
set -ex
[ -z "$(git diff-tree --no-commit-id --name-only -r HEAD --\
. \
':(exclude)CHANGELOG.md' \
':(exclude)src/node_version.h' \
':(exclude)test/parallel/test-process-release.js' \
':(exclude)doc/abi_version_registry.json' \
':(exclude)doc/api/' \
":(exclude)doc/changelogs/")" ]
- name: Validate CHANGELOG
id: releaser-info
run: |
EXPECTED_CHANGELOG_TITLE_INTRO="## $COMMIT_SUBJECT, @"
echo "Expected CHANGELOG section title: $EXPECTED_CHANGELOG_TITLE_INTRO"
CHANGELOG_PATH="doc/changelogs/CHANGELOG_V${MAJOR}.md"
CHANGELOG_TITLE="$(grep "$EXPECTED_CHANGELOG_TITLE_INTRO" "$CHANGELOG_PATH")"
echo "Actual: $CHANGELOG_TITLE"
[ "${CHANGELOG_TITLE%%@*}@" = "$EXPECTED_CHANGELOG_TITLE_INTRO" ]
gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
--jq '.commits.[] | { smallSha: .sha[0:10] } + (.commit.message|capture("^(?<title>.+)\n\n(.*\n)*PR-URL: (?<prURL>.+)(\n|$)"))' \
"/repos/${GITHUB_REPOSITORY}/compare/v${MAJOR}.x...$GITHUB_SHA" --paginate \
| node tools/actions/lint-release-proposal-commit-list.mjs "$CHANGELOG_PATH" "$GITHUB_SHA" \
| while IFS= read -r PR_URL; do
DONT_LAND_LABEL="dont-land-on-v${MAJOR}.x" LTS_WATCH_LABEL="lts-watch-v${MAJOR}.x" gh pr view \
--json labels,url \
--jq '
if (.labels|any(.name==env.DONT_LAND_LABEL)) then
error("\(.url) has the \(env.DONT_LAND_LABEL) label, forbidding it to be in this release proposal")
elif (.labels|any(.name==env.LTS_WATCH_LABEL)) then
error("\(.url) has the \(env.LTS_WATCH_LABEL) label, please remove the label now that the PR is included in a release proposal")
end
' \
"$PR_URL" > /dev/null
done
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.
env:
COMMIT_SUBJECT: ${{ steps.commit-message-parse.outputs.COMMIT_SUBJECT }}
MAJOR: ${{ steps.commit-message-parse.outputs.MAJOR }}
GH_TOKEN: ${{ github.token }}