◐ Shell
clean mode source ↗

tools: update `create-release-proposal` workflow · nodejs/node@12baefb

@@ -2,6 +2,9 @@

2233

set -xe

445+

GITHUB_REPOSITORY=${GITHUB_REPOSITORY:-nodejs/node}

6+

BOT_TOKEN=${BOT_TOKEN:-}

7+58

RELEASE_DATE=$1

69

RELEASE_LINE=$2

710

@@ -10,24 +13,114 @@ if [ -z "$RELEASE_DATE" ] || [ -z "$RELEASE_LINE" ]; then

1013

exit 1

1114

fi

121516+

if [ -z "$GITHUB_REPOSITORY" ] || [ -z "$BOT_TOKEN" ]; then

17+

echo "Invalid value in env for GITHUB_REPOSITORY and BOT_TOKEN"

18+

exit 1

19+

fi

20+21+

if ! command -v node || ! command -v gh || ! command -v git || ! command -v awk; then

22+

echo "Missing required dependencies"

23+

exit 1

24+

fi

25+1326

git node release --prepare --skipBranchDiff --yes --releaseDate "$RELEASE_DATE"

14-

# We use it to not specify the branch name as it changes based on

15-

# the commit list (semver-minor/semver-patch)

16-

git config push.default current

17-

git push

27+28+

HEAD_BRANCH="$(git rev-parse --abbrev-ref HEAD)"

29+

HEAD_SHA="$(git rev-parse HEAD^)"

18301931

TITLE=$(awk "/^## ${RELEASE_DATE}/ { print substr(\$0, 4) }" "doc/changelogs/CHANGELOG_V${RELEASE_LINE}.md")

20322133

# Use a temporary file for the PR body

2234

TEMP_BODY="$(awk "/## ${RELEASE_DATE}/,/^<a id=/{ if (!/^<a id=/) print }" "doc/changelogs/CHANGELOG_V${RELEASE_LINE}.md")"

233524-

PR_URL="$(gh pr create --title "$TITLE" --body "$TEMP_BODY" --base "v$RELEASE_LINE.x")"

36+

# Create the proposal branch

37+

gh api \

38+

--method POST \

39+

-H "Accept: application/vnd.github+json" \

40+

-H "X-GitHub-Api-Version: 2022-11-28" \

41+

"/repos/${GITHUB_REPOSITORY}/git/refs" \

42+

-f "ref=refs/heads/$HEAD_BRANCH" -f "sha=$HEAD_SHA"

43+44+

# Create the proposal PR

45+

PR_URL="$(gh api \

46+

--method POST \

47+

--jq .html_url \

48+

-H "Accept: application/vnd.github+json" \

49+

-H "X-GitHub-Api-Version: 2022-11-28" \

50+

"/repos/${GITHUB_REPOSITORY}/pulls" \

51+

-f "title=$TITLE" -f "body=$TEMP_BODY" -f "head=$HEAD_BRANCH" -f "base=v$RELEASE_LINE.x")"

255226-

# Amend commit message so it contains the correct PR-URL trailer.

27-

AMENDED_COMMIT_MSG="$(git log -1 --pretty=%B | sed "s|PR-URL: TODO|PR-URL: $PR_URL|")"

53+

# Push the release commit to the proposal branch using `BOT_TOKEN` from the env

54+

node --input-type=module - \

55+

"$GITHUB_REPOSITORY" \

56+

"$HEAD_BRANCH" \

57+

"$HEAD_SHA" \

58+

"$(git log -1 HEAD --format=%s || true)" \

59+

"$(git log -1 HEAD --format=%b | awk -v PR_URL="$PR_URL" '{sub(/^PR-URL: TODO$/, "PR-URL: " PR_URL)} 1' || true)" \

60+

"$(git show HEAD --diff-filter=d --name-only --format= || true)" \

61+

"$(git show HEAD --diff-filter=D --name-only --format= || true)" \

62+

<<'EOF'

63+

const [,,

64+

repo,

65+

branch,

66+

parentCommitSha,

67+

commit_title,

68+

commit_body,

69+

modifiedOrAddedFiles,

70+

deletedFiles,

71+

] = process.argv;

287229-

# Replace "TODO" with the PR URL in the last commit

30-

git commit --amend --no-edit -m "$AMENDED_COMMIT_MSG" || true

73+

import { readFileSync } from 'node:fs';

74+

import util from 'node:util';

317532-

# Force-push the amended commit

33-

git push --force

76+

const query = `

77+

mutation ($repo: String! $branch: String!, $parentCommitSha: GitObjectID!, $changes: FileChanges!, $commit_title: String!, $commit_body: String) {

78+

createCommitOnBranch(input: {

79+

branch: {

80+

repositoryNameWithOwner: $repo,

81+

branchName: $branch

82+

},

83+

message: {

84+

headline: $commit_title,

85+

body: $commit_body

86+

},

87+

expectedHeadOid: $parentCommitSha,

88+

fileChanges: $changes

89+

}) {

90+

commit {

91+

url

92+

}

93+

}

94+

}

95+

`;

96+

const response = await fetch('https://api.github.com/graphql', {

97+

method: 'POST',

98+

headers: {

99+

'Authorization': `bearer ${process.env.BOT_TOKEN}`,

100+

},

101+

body: JSON.stringify({

102+

query,

103+

variables: {

104+

repo,

105+

branch,

106+

parentCommitSha,

107+

commit_title,

108+

commit_body,

109+

changes: {

110+

additions: modifiedOrAddedFiles.split('\n').filter(Boolean)

111+

.map(path => ({ path, contents: readFileSync(path).toString('base64') })),

112+

deletions: deletedFiles.split('\n').filter(Boolean),

113+

}

114+

},

115+

})

116+

});

117+

if (!response.ok) {

118+

console.log({statusCode: response.status, statusText: response.statusText});

119+

process.exitCode ||= 1;

120+

}

121+

const data = await response.json();

122+

if (data.errors?.length) {

123+

throw new Error('Endpoint returned an error', { cause: data });

124+

}

125+

console.log(util.inspect(data, { depth: Infinity }));

126+

EOF