Files
node/tools/actions/create-release-proposal.sh
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

142 lines
3.9 KiB
Bash
Executable File

#!/bin/sh
GITHUB_REPOSITORY=${GITHUB_REPOSITORY:-nodejs/node}
BOT_TOKEN=${BOT_TOKEN:-}
RELEASE_DATE=$1
RELEASE_LINE=$2
RELEASER=$3
RELEASE_VERSION=$4
if [ -z "$RELEASE_DATE" ] || [ -z "$RELEASE_LINE" ]; then
echo "Usage: $0 <RELEASE_DATE> <RELEASE_LINE> [<RELEASER> [<RELEASE_VERSION>]]"
exit 1
fi
if [ -z "$GITHUB_REPOSITORY" ] || [ -z "$BOT_TOKEN" ]; then
echo "Invalid value in env for GITHUB_REPOSITORY and BOT_TOKEN"
exit 1
fi
HAS_MISSING_DEPS=
for dep in node gh git git-node awk; do
command -v "$dep" > /dev/null || {
echo "Required dependency $dep is missing" >&2
HAS_MISSING_DEPS=1
}
done
[ -z "$HAS_MISSING_DEPS" ] || exit 1
set -xe
git node release --prepare --skipBranchDiff --yes --releaseDate "$RELEASE_DATE" \
${RELEASE_VERSION:+--newVersion "$RELEASE_VERSION"}
HEAD_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
HEAD_SHA="$(git rev-parse HEAD^)"
TITLE="$(git log -1 --format=%s)"
# GH rest API has an undocumented limit of 65536 char for the body, setting it
# to 65534 to account for the ellipsis and the final EOL.
TEMP_BODY="$(awk -v MAX_BODY_LENGTH="65534" \
"/^## ${RELEASE_DATE}/,/^<a id=/{
if (/^<a id=/) {exit;}
if ((output_length += length(\$0) + 1) > MAX_BODY_LENGTH) {exit 1;}
print
}" "doc/changelogs/CHANGELOG_V${RELEASE_LINE}.md" || echo "…")"
# Create the proposal branch
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${GITHUB_REPOSITORY}/git/refs" \
-f "ref=refs/heads/$HEAD_BRANCH" -f "sha=$HEAD_SHA"
# Create the proposal PR
PR_URL="$(gh api \
--method POST \
--jq .html_url \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${GITHUB_REPOSITORY}/pulls" \
-f "title=$TITLE" -f "body=$TEMP_BODY" -f "head=$HEAD_BRANCH" -f "base=v$RELEASE_LINE.x" -f draft=true)"
# Push the release commit to the proposal branch using `BOT_TOKEN` from the env
node --input-type=module - \
"$GITHUB_REPOSITORY" \
"$HEAD_BRANCH" \
"$HEAD_SHA" \
"$TITLE" \
"$(git log -1 HEAD --format=%b | awk -v PR_URL="$PR_URL" '{sub(/^PR-URL: TODO$/, "PR-URL: " PR_URL)} 1' || true)" \
"$(git show HEAD --diff-filter=d --name-only --format= || true)" \
"$(git show HEAD --diff-filter=D --name-only --format= || true)" \
<<'EOF'
const [,,
repo,
branch,
parentCommitSha,
commit_title,
commit_body,
modifiedOrAddedFiles,
deletedFiles,
] = process.argv;
import { readFileSync } from 'node:fs';
import util from 'node:util';
const query = `
mutation ($repo: String! $branch: String!, $parentCommitSha: GitObjectID!, $changes: FileChanges!, $commit_title: String!, $commit_body: String) {
createCommitOnBranch(input: {
branch: {
repositoryNameWithOwner: $repo,
branchName: $branch
},
message: {
headline: $commit_title,
body: $commit_body
},
expectedHeadOid: $parentCommitSha,
fileChanges: $changes
}) {
commit {
url
}
}
}
`;
const response = await fetch('https://api.github.com/graphql', {
method: 'POST',
headers: {
'Authorization': `bearer ${process.env.BOT_TOKEN}`,
},
body: JSON.stringify({
query,
variables: {
repo,
branch,
parentCommitSha,
commit_title,
commit_body,
changes: {
additions: modifiedOrAddedFiles.split('\n').filter(Boolean)
.map(path => ({ path, contents: readFileSync(path).toString('base64') })),
deletions: deletedFiles.split('\n').filter(Boolean),
}
},
})
});
if (!response.ok) {
console.log({statusCode: response.status, statusText: response.statusText});
process.exitCode ||= 1;
}
const data = await response.json();
if (data.errors?.length) {
throw new Error('Endpoint returned an error', { cause: data });
}
console.log(util.inspect(data, { depth: Infinity }));
EOF
gh pr edit "$PR_URL" --add-label release --add-label "v$RELEASE_LINE.x" --add-assignee "$RELEASER"