Skip to content

Commit e210360

Browse files
authored
Add create release branch workflow (#81687)
This creates a new workflow to create a release branch and apply the necessary configs to allow releasing from that branch as this is quite common for backports.
1 parent 3c2a195 commit e210360

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
on:
2+
workflow_dispatch:
3+
inputs:
4+
branchName:
5+
description: name of branch to create (next-15-4)
6+
required: true
7+
type: string
8+
9+
tagName:
10+
description: Tag to start the branch from (v15.4.1)
11+
type: string
12+
required: true
13+
14+
secrets:
15+
RELEASE_BOT_GITHUB_TOKEN:
16+
required: true
17+
18+
name: Create Release Branch
19+
20+
env:
21+
NAPI_CLI_VERSION: 2.18.4
22+
TURBO_VERSION: 2.3.3
23+
NODE_LTS_VERSION: 20
24+
25+
jobs:
26+
start:
27+
if: github.repository_owner == 'vercel'
28+
runs-on: ubuntu-latest
29+
env:
30+
NEXT_TELEMETRY_DISABLED: 1
31+
# we build a dev binary for use in CI so skip downloading
32+
# canary next-swc binaries in the monorepo
33+
NEXT_SKIP_NATIVE_POSTINSTALL: 1
34+
35+
steps:
36+
- name: Setup node
37+
uses: actions/setup-node@v4
38+
with:
39+
node-version: 18
40+
check-latest: true
41+
42+
- name: Clone Next.js repository
43+
run: git clone https://github.com/vercel/next.js.git --depth=25 --single-branch --branch ${GITHUB_REF_NAME:-canary} .
44+
45+
- name: Check token
46+
run: gh auth status
47+
env:
48+
GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
49+
50+
# https://github.com/actions/virtual-environments/issues/1187
51+
- name: tune linux network
52+
run: sudo ethtool -K eth0 tx off rx off
53+
54+
- name: Setup corepack
55+
run: |
56+
npm i -g corepack@0.31
57+
corepack enable
58+
pnpm --version
59+
60+
- id: get-store-path
61+
run: echo STORE_PATH=$(pnpm store path) >> $GITHUB_OUTPUT
62+
63+
- uses: actions/cache@v4
64+
timeout-minutes: 5
65+
id: cache-pnpm-store
66+
with:
67+
path: ${{ steps.get-store-path.outputs.STORE_PATH }}
68+
key: pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
69+
restore-keys: |
70+
pnpm-store-
71+
pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
72+
73+
- run: pnpm install
74+
75+
- run: node ./scripts/create-release-branch.js --branch-name ${{ github.event.inputs.branchName }} --tag-name ${{ github.event.inputs.tagName }}
76+
env:
77+
RELEASE_BOT_GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}

scripts/create-release-branch.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// @ts-check
2+
const fs = require('fs')
3+
const path = require('path')
4+
const execa = require('execa')
5+
const resolveFrom = require('resolve-from')
6+
7+
async function main() {
8+
const args = process.argv
9+
const branchName = args[args.indexOf('--branch-name') + 1]
10+
const tagName = args[args.indexOf('--tag-name') + 1]
11+
12+
if (!branchName) {
13+
throw new Error('branchName value is missing!')
14+
}
15+
16+
if (!tagName || !tagName.startsWith('v')) {
17+
throw new Error('tagName value is invalid "' + tagName + '"')
18+
}
19+
20+
const githubToken = process.env.RELEASE_BOT_GITHUB_TOKEN
21+
22+
if (!githubToken) {
23+
console.log(`Missing RELEASE_BOT_GITHUB_TOKEN`)
24+
return
25+
}
26+
27+
const configStorePath = resolveFrom(
28+
path.join(process.cwd(), 'node_modules/release'),
29+
'configstore'
30+
)
31+
const ConfigStore = require(configStorePath)
32+
33+
const config = new ConfigStore('release')
34+
config.set('token', githubToken)
35+
36+
await execa(
37+
`git remote set-url origin https://nextjs-bot:${githubToken}@github.com/vercel/next.js.git`,
38+
{ stdio: 'inherit', shell: true }
39+
)
40+
await execa(`git config user.name "nextjs-bot"`, {
41+
stdio: 'inherit',
42+
shell: true,
43+
})
44+
await execa(`git config user.email "it+nextjs-bot@vercel.com"`, {
45+
stdio: 'inherit',
46+
shell: true,
47+
})
48+
await execa(`git checkout -b "${branchName}"`, {
49+
stdio: 'inherit',
50+
shell: true,
51+
})
52+
await execa(`git reset --hard ${tagName}`, {
53+
stdio: 'inherit',
54+
shell: true,
55+
})
56+
const lernaPath = path.join(__dirname, '..', 'lerna.json')
57+
const existingLerna = JSON.parse(
58+
await fs.promises.readFile(lernaPath, 'utf8')
59+
)
60+
existingLerna.command.publish.allowBranch.push(branchName)
61+
62+
await fs.promises.writeFile(lernaPath, JSON.stringify(existingLerna, null, 2))
63+
64+
const buildAndDeployPath = path.join(
65+
__dirname,
66+
'..',
67+
'.github',
68+
'workflows',
69+
'build_and_deploy.yml'
70+
)
71+
const buildAndDeploy = await fs.promises.readFile(buildAndDeployPath, 'utf8')
72+
await fs.promises.writeFile(
73+
buildAndDeployPath,
74+
buildAndDeploy.replace(/refs\/heads\/canary/g, `refs/heads/${branchName}`)
75+
)
76+
77+
const buildAndTestPath = path.join(
78+
__dirname,
79+
'..',
80+
'.github',
81+
'workflows',
82+
'build_and_test.yml'
83+
)
84+
const buildAndTest = await fs.promises.readFile(buildAndTestPath, 'utf8')
85+
await fs.promises.writeFile(
86+
buildAndTestPath,
87+
buildAndTest.replace(`['canary']`, `['${branchName}']`)
88+
)
89+
90+
await execa(`git add .`, {
91+
stdio: 'inherit',
92+
shell: true,
93+
})
94+
await execa(`git commit -m "setup release branch"`, {
95+
stdio: 'inherit',
96+
shell: true,
97+
})
98+
await execa(`git push origin "${branchName}"`, {
99+
stdio: 'inherit',
100+
shell: true,
101+
})
102+
103+
console.log(`Waiting 5s before updating branch rules`)
104+
await new Promise((resolve) => setTimeout(resolve, 5_000))
105+
106+
const updateEnvironmentRes = await fetch(
107+
'https://api.github.com/repos/vercel/next.js/environments/release-stable/deployment-branch-policies',
108+
{
109+
method: 'POST',
110+
headers: {
111+
Accept: 'application/vnd.github+json',
112+
Authorization: `Bearer ${githubToken}`,
113+
'X-GitHub-Api-Version': '2022-11-28',
114+
},
115+
body: JSON.stringify({ name: branchName }),
116+
}
117+
)
118+
119+
if (!updateEnvironmentRes.ok) {
120+
console.error(
121+
{ status: updateEnvironmentRes.status },
122+
await updateEnvironmentRes.text()
123+
)
124+
throw new Error(`Failed to update environment branch rules`)
125+
}
126+
console.log(`Successfully updated deployment environment branch rules`)
127+
}
128+
129+
main()

0 commit comments

Comments
 (0)