Skip to content

Commit bc6598f

Browse files
authored
Add tarball deployments for each build (vercel#62969)
This adds automatic tarball deployments for each build so that we don't have to trigger a canary release whenever we want to create a full tarball with all `next-swc` builds. Example tarball https://github.com/vercel/next.js/actions/runs/8239762627/job/22534810077#step:9:34 Closes NEXT-2735
1 parent de5ea0f commit bc6598f

File tree

3 files changed

+276
-0
lines changed

3 files changed

+276
-0
lines changed

.github/workflows/build_and_deploy.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,52 @@ jobs:
392392
name: wasm-binaries-${{matrix.target}}
393393
path: packages/next-swc/crates/wasm/pkg-*
394394

395+
deployTarball:
396+
if: ${{ needs.build.outputs.isRelease != 'true' }}
397+
name: Deploy tarball
398+
runs-on: ubuntu-latest
399+
needs:
400+
- build
401+
- build-wasm
402+
- build-native
403+
env:
404+
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
405+
VERCEL_TEST_TEAM: vtest314-next-e2e-tests
406+
steps:
407+
- name: Setup node
408+
uses: actions/setup-node@v4
409+
with:
410+
node-version: ${{ env.NODE_LTS_VERSION }}
411+
check-latest: true
412+
- run: corepack enable
413+
414+
# https://github.com/actions/virtual-environments/issues/1187
415+
- name: tune linux network
416+
run: sudo ethtool -K eth0 tx off rx off
417+
418+
- uses: actions/cache@v4
419+
timeout-minutes: 5
420+
id: restore-build
421+
with:
422+
path: ./*
423+
key: ${{ github.sha }}-${{ github.run_number }}
424+
425+
- uses: actions/download-artifact@v4
426+
with:
427+
pattern: next-swc-binaries-*
428+
merge-multiple: true
429+
path: packages/next-swc/native
430+
431+
- uses: actions/download-artifact@v4
432+
with:
433+
pattern: wasm-binaries-*
434+
merge-multiple: true
435+
path: packages/next-swc/crates/wasm
436+
437+
- run: npm i -g vercel@latest
438+
439+
- run: node ./scripts/deploy-tarball.js
440+
395441
publishRelease:
396442
if: ${{ needs.build.outputs.isRelease == 'true' }}
397443
name: Potentially publish release

scripts/deploy-tarball.js

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// @ts-check
2+
const path = require('path')
3+
const execa = require('execa')
4+
const fs = require('fs/promises')
5+
6+
const cwd = process.cwd()
7+
8+
async function main() {
9+
const deployDir = path.join(cwd, 'files')
10+
const publicDir = path.join(deployDir, 'public')
11+
await fs.mkdir(publicDir, { recursive: true })
12+
await fs.writeFile(
13+
path.join(deployDir, 'package.json'),
14+
JSON.stringify({
15+
name: 'files',
16+
dependencies: {},
17+
scripts: {
18+
build: 'node inject-deploy-url.js',
19+
},
20+
})
21+
)
22+
await fs.copyFile(
23+
path.join(cwd, 'scripts/inject-deploy-url.js'),
24+
path.join(deployDir, 'inject-deploy-url.js')
25+
)
26+
27+
let nativePackagesDir = path.join(cwd, 'packages/next-swc/crates/napi/npm')
28+
let platforms = (await fs.readdir(nativePackagesDir)).filter(
29+
(name) => !name.startsWith('.')
30+
)
31+
32+
const optionalDeps = {}
33+
const { version } = JSON.parse(
34+
await fs.readFile(path.join(cwd, 'lerna.json'), 'utf8')
35+
)
36+
37+
await Promise.all(
38+
platforms.map(async (platform) => {
39+
let binaryName = `next-swc.${platform}.node`
40+
await fs.cp(
41+
path.join(cwd, 'packages/next-swc/native', binaryName),
42+
path.join(nativePackagesDir, platform, binaryName)
43+
)
44+
let pkg = JSON.parse(
45+
await fs.readFile(
46+
path.join(nativePackagesDir, platform, 'package.json'),
47+
'utf8'
48+
)
49+
)
50+
pkg.version = version
51+
await fs.writeFile(
52+
path.join(nativePackagesDir, platform, 'package.json'),
53+
JSON.stringify(pkg, null, 2)
54+
)
55+
const { stdout } = await execa(`npm`, [
56+
`pack`,
57+
`${path.join(nativePackagesDir, platform)}`,
58+
])
59+
process.stdout.write(stdout)
60+
const tarballName = stdout.split('\n').pop()?.trim() || ''
61+
await fs.rename(
62+
path.join(cwd, tarballName),
63+
path.join(publicDir, tarballName)
64+
)
65+
optionalDeps[pkg.name] = `https://DEPLOY_URL/${tarballName}`
66+
})
67+
)
68+
69+
const nextPkgJsonPath = path.join(cwd, 'packages/next/package.json')
70+
const nextPkg = JSON.parse(await fs.readFile(nextPkgJsonPath, 'utf8'))
71+
72+
nextPkg.optionalDependencies = optionalDeps
73+
74+
await fs.writeFile(nextPkgJsonPath, JSON.stringify(nextPkg, null, 2))
75+
76+
const { stdout: nextPackStdout } = await execa(`npm`, [
77+
`pack`,
78+
`${path.join(cwd, 'packages/next')}`,
79+
])
80+
process.stdout.write(nextPackStdout)
81+
const nextTarballName = nextPackStdout.split('\n').pop()?.trim() || ''
82+
await fs.rename(
83+
path.join(cwd, nextTarballName),
84+
path.join(publicDir, nextTarballName)
85+
)
86+
87+
await fs.writeFile(
88+
path.join(deployDir, 'vercel.json'),
89+
JSON.stringify(
90+
{
91+
version: 2,
92+
rewrites: [
93+
{
94+
source: '/next.tgz',
95+
destination: `/${nextTarballName}`,
96+
},
97+
],
98+
},
99+
null,
100+
2
101+
)
102+
)
103+
const vercelConfigDir = path.join(cwd, '.vercel')
104+
105+
if (process.env.VERCEL_TEST_TOKEN) {
106+
await fs.mkdir(vercelConfigDir)
107+
await fs.writeFile(
108+
path.join(vercelConfigDir, 'auth.json'),
109+
JSON.stringify({
110+
token: process.env.VERCEL_TEST_TOKEN,
111+
})
112+
)
113+
await fs.writeFile(
114+
path.join(vercelConfigDir, 'config.json'),
115+
JSON.stringify({})
116+
)
117+
console.log('wrote config to', vercelConfigDir)
118+
}
119+
120+
const child = execa(
121+
'vercel',
122+
[
123+
'--scope',
124+
process.env.VERCEL_TEST_TEAM || '',
125+
'--global-config',
126+
vercelConfigDir,
127+
'-y',
128+
],
129+
{
130+
cwd: deployDir,
131+
}
132+
)
133+
let deployOutput = ''
134+
const handleData = (type) => (chunk) => {
135+
process[type].write(chunk)
136+
137+
// only want stdout since that's where deployment URL
138+
// is sent to
139+
if (type === 'stdout') {
140+
deployOutput += chunk.toString()
141+
}
142+
}
143+
child.stdout?.on('data', handleData('stdout'))
144+
child.stderr?.on('data', handleData('stderr'))
145+
146+
await child
147+
148+
const deployUrl = deployOutput.trim()
149+
console.log(`\n\nNext.js tarball: ${deployUrl.trim()}/next.tgz`)
150+
}
151+
152+
main().catch((err) => {
153+
console.error(err)
154+
process.exit(1)
155+
})

scripts/inject-deploy-url.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
const path = require('path')
2+
const { spawn } = require('child_process')
3+
const fs = require('fs/promises')
4+
5+
const cwd = process.cwd()
6+
7+
async function main() {
8+
const tarballs = await fs.readdir(path.join(cwd, 'public'))
9+
const nextTarball = tarballs.find((item) => !item.includes('-swc'))
10+
11+
await fs.rename(
12+
path.join(cwd, 'public', nextTarball),
13+
path.join(cwd, nextTarball)
14+
)
15+
16+
await new Promise((resolve, reject) => {
17+
const child = spawn('tar', ['-xf', nextTarball], {
18+
stdio: 'inherit',
19+
shell: true,
20+
cwd,
21+
})
22+
23+
child.on('exit', (code) => {
24+
if (code) {
25+
return reject(`Failed with code ${code}`)
26+
}
27+
resolve()
28+
})
29+
})
30+
31+
const unpackedPackageJson = path.join(cwd, 'package/package.json')
32+
const parsedPackageJson = JSON.parse(
33+
await fs.readFile(unpackedPackageJson, 'utf8')
34+
)
35+
const { optionalDependencies } = parsedPackageJson
36+
37+
for (const key of Object.keys(optionalDependencies)) {
38+
optionalDependencies[key] = optionalDependencies[key].replace(
39+
'DEPLOY_URL',
40+
process.env.VERCEL_URL
41+
)
42+
}
43+
44+
await fs.writeFile(
45+
unpackedPackageJson,
46+
JSON.stringify(parsedPackageJson, null, 2)
47+
)
48+
49+
await fs.unlink(nextTarball)
50+
51+
await new Promise((resolve, reject) => {
52+
const child = spawn('tar', ['-czf', nextTarball, 'package'], {
53+
stdio: 'inherit',
54+
shell: true,
55+
cwd,
56+
})
57+
58+
child.on('exit', (code) => {
59+
if (code) {
60+
return reject(`Failed with code ${code}`)
61+
}
62+
resolve()
63+
})
64+
})
65+
66+
await fs.rename(
67+
path.join(cwd, nextTarball),
68+
path.join(cwd, 'public', nextTarball)
69+
)
70+
}
71+
72+
main().catch((err) => {
73+
console.error(err)
74+
process.exit(1)
75+
})

0 commit comments

Comments
 (0)