Skip to content

Commit 6b863fe

Browse files
committed
Apply publish step optimizations (#43620)
Follow-up to #32337 this removes the un-necessary step where we fetch all of the tags which requires pulling a lot of un-necessary git history inflating cache size and publish times. The only reason these tags were needing to be fetched is due to an issue in how the `actions/checkout` step works (actions/checkout#882). This reduces the publish times by at least 4 minutes by removing the tags fetching step https://github.com/vercel/next.js/actions/runs/3598569786/jobs/6061449995#step:16:14 As a further optimization this adds concurrency to the `npm publish` calls themselves to hopefully reduce time spent there as well.
1 parent 2063ff3 commit 6b863fe

File tree

5 files changed

+166
-129
lines changed

5 files changed

+166
-129
lines changed

.github/workflows/build_test_deploy.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,10 @@ jobs:
8484
- run: pnpm install
8585
- run: pnpm run build
8686
- run: node run-tests.js --timings --write-timings -g 1/1
87-
- run: node ./scripts/fetch-tags.mjs ${{ github.sha }}
8887

8988
- id: check-release
9089
run: |
91-
if [[ $(git describe --exact-match 2> /dev/null || :) = v* ]];
90+
if [[ $(node ./scripts/check-is-release.js 2> /dev/null || :) = v* ]];
9291
then
9392
echo "::set-output name=IS_RELEASE::true"
9493
else
@@ -893,7 +892,7 @@ jobs:
893892

894893
- run: npm i -g pnpm@${PNPM_VERSION}
895894
- run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
896-
- run: ./scripts/publish-native.js $GITHUB_REF
895+
- run: ./scripts/publish-native.js
897896
- run: ./scripts/publish-release.js
898897

899898
testDeployE2E:
Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { execSync } from 'child_process'
2-
import execa from 'execa'
3-
;(async () => {
1+
const { execSync } = require('child_process')
2+
3+
const checkIsRelease = async () => {
44
let commitId = process.argv[2] || ''
55

66
// parse only the last string which should be version if
@@ -14,21 +14,13 @@ import execa from 'execa'
1414
const versionString = commitMsg.split(' ').pop().trim()
1515
const publishMsgRegex = /^v\d{1,}\.\d{1,}\.\d{1,}(-\w{1,}\.\d{1,})?$/
1616

17-
console.log({ commitId, commitMsg, versionString })
18-
1917
if (publishMsgRegex.test(versionString)) {
20-
console.log('publish commit, fetching tags')
21-
22-
const result = await execa(
23-
'git',
24-
['fetch', '--depth=1', 'origin', '+refs/tags/*:refs/tags/*'],
25-
{
26-
stdio: ['ignore', 'inherit', 'inherit'],
27-
}
28-
)
29-
30-
process.exit(result.exitCode)
18+
console.log(versionString)
19+
process.exit(0)
3120
} else {
32-
console.log('not publish commit')
21+
console.log('not publish commit', { commitId, commitMsg, versionString })
22+
process.exit(1)
3323
}
34-
})()
24+
}
25+
26+
checkIsRelease()

scripts/publish-native.js

Lines changed: 110 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,137 @@
11
#!/usr/bin/env node
22

33
const path = require('path')
4-
const { readFile, readdir, writeFile } = require('fs/promises')
4+
const execa = require('execa')
55
const { copy } = require('fs-extra')
6-
const { execSync } = require('child_process')
6+
const { Sema } = require('async-sema')
7+
const { readFile, readdir, writeFile } = require('fs/promises')
78

89
const cwd = process.cwd()
910

1011
;(async function () {
1112
try {
13+
const publishSema = new Sema(2)
14+
1215
let version = JSON.parse(
1316
await readFile(path.join(cwd, 'lerna.json'))
1417
).version
15-
let gitref = process.argv.slice(2)[0]
1618

1719
// Copy binaries to package folders, update version, and publish
1820
let nativePackagesDir = path.join(cwd, 'packages/next-swc/crates/napi/npm')
1921
let platforms = (await readdir(nativePackagesDir)).filter(
2022
(name) => !name.startsWith('.')
2123
)
2224

23-
for (let platform of platforms) {
24-
try {
25-
let binaryName = `next-swc.${platform}.node`
26-
await copy(
27-
path.join(cwd, 'packages/next-swc/native', binaryName),
28-
path.join(nativePackagesDir, platform, binaryName)
29-
)
30-
let pkg = JSON.parse(
31-
await readFile(path.join(nativePackagesDir, platform, 'package.json'))
32-
)
33-
pkg.version = version
34-
await writeFile(
35-
path.join(nativePackagesDir, platform, 'package.json'),
36-
JSON.stringify(pkg, null, 2)
37-
)
38-
execSync(
39-
`npm publish ${path.join(
40-
nativePackagesDir,
41-
platform
42-
)} --access public ${
43-
gitref.includes('canary') ? ' --tag canary' : ''
44-
}`
45-
)
46-
} catch (err) {
47-
// don't block publishing other versions on single platform error
48-
console.error(`Failed to publish`, platform)
25+
await Promise.all(
26+
platforms.map(async (platform) => {
27+
await publishSema.acquire()
4928

50-
if (
51-
err.message &&
52-
err.message.includes(
53-
'You cannot publish over the previously published versions'
29+
try {
30+
let binaryName = `next-swc.${platform}.node`
31+
await copy(
32+
path.join(cwd, 'packages/next-swc/native', binaryName),
33+
path.join(nativePackagesDir, platform, binaryName)
34+
)
35+
let pkg = JSON.parse(
36+
await readFile(
37+
path.join(nativePackagesDir, platform, 'package.json')
38+
)
5439
)
55-
) {
56-
console.error('Ignoring already published error', platform)
57-
} else {
58-
// throw err
40+
pkg.version = version
41+
await writeFile(
42+
path.join(nativePackagesDir, platform, 'package.json'),
43+
JSON.stringify(pkg, null, 2)
44+
)
45+
await execa(
46+
`npm`,
47+
[
48+
`publish`,
49+
`${path.join(nativePackagesDir, platform)}`,
50+
`--access`,
51+
`public`,
52+
...(version.includes('canary') ? ['--tag', 'canary'] : []),
53+
],
54+
{ stdio: 'inherit' }
55+
)
56+
} catch (err) {
57+
// don't block publishing other versions on single platform error
58+
console.error(`Failed to publish`, platform, err)
59+
60+
if (
61+
err.message &&
62+
err.message.includes(
63+
'You cannot publish over the previously published versions'
64+
)
65+
) {
66+
console.error('Ignoring already published error', platform, err)
67+
} else {
68+
// throw err
69+
}
70+
} finally {
71+
publishSema.release()
5972
}
60-
}
61-
// lerna publish in next step sill fail if git status is not clean
62-
execSync(
63-
`git update-index --skip-worktree ${path.join(
64-
nativePackagesDir,
65-
platform,
66-
'package.json'
67-
)}`
68-
)
69-
}
73+
// lerna publish in next step sill fail if git status is not clean
74+
await execa(
75+
`git`,
76+
[
77+
'update-index',
78+
'--skip-worktree',
79+
`${path.join(nativePackagesDir, platform, 'package.json')}`,
80+
],
81+
{ stdio: 'inherit' }
82+
)
83+
})
84+
)
7085

7186
// Update name/version of wasm packages and publish
7287
let wasmDir = path.join(cwd, 'packages/next-swc/crates/wasm')
73-
for (let wasmTarget of ['web', 'nodejs']) {
74-
let wasmPkg = JSON.parse(
75-
await readFile(path.join(wasmDir, `pkg-${wasmTarget}/package.json`))
76-
)
77-
wasmPkg.name = `@next/swc-wasm-${wasmTarget}`
78-
wasmPkg.version = version
7988

80-
await writeFile(
81-
path.join(wasmDir, `pkg-${wasmTarget}/package.json`),
82-
JSON.stringify(wasmPkg, null, 2)
83-
)
89+
await Promise.all(
90+
['web', 'nodejs'].map(async (wasmTarget) => {
91+
await publishSema.acquire()
8492

85-
try {
86-
execSync(
87-
`npm publish ${path.join(
88-
wasmDir,
89-
`pkg-${wasmTarget}`
90-
)} --access public ${
91-
gitref.includes('canary') ? ' --tag canary' : ''
92-
}`
93+
let wasmPkg = JSON.parse(
94+
await readFile(path.join(wasmDir, `pkg-${wasmTarget}/package.json`))
95+
)
96+
wasmPkg.name = `@next/swc-wasm-${wasmTarget}`
97+
wasmPkg.version = version
98+
99+
await writeFile(
100+
path.join(wasmDir, `pkg-${wasmTarget}/package.json`),
101+
JSON.stringify(wasmPkg, null, 2)
93102
)
94-
} catch (err) {
95-
// don't block publishing other versions on single platform error
96-
console.error(`Failed to publish`, wasmTarget)
97103

98-
if (
99-
err.message &&
100-
err.message.includes(
101-
'You cannot publish over the previously published versions'
104+
try {
105+
await execa(
106+
`npm`,
107+
[
108+
'publish',
109+
`${path.join(wasmDir, `pkg-${wasmTarget}`)}`,
110+
'--access',
111+
'public',
112+
...(version.includes('canary') ? ['--tag', 'canary'] : []),
113+
],
114+
{ stdio: 'inherit' }
102115
)
103-
) {
104-
console.error('Ignoring already published error', wasmTarget)
105-
} else {
106-
// throw err
116+
} catch (err) {
117+
// don't block publishing other versions on single platform error
118+
console.error(`Failed to publish`, wasmTarget, err)
119+
120+
if (
121+
err.message &&
122+
err.message.includes(
123+
'You cannot publish over the previously published versions'
124+
)
125+
) {
126+
console.error('Ignoring already published error', wasmTarget)
127+
} else {
128+
// throw err
129+
}
130+
} finally {
131+
publishSema.release()
107132
}
108-
}
109-
}
133+
})
134+
)
110135

111136
// Update optional dependencies versions
112137
let nextPkg = JSON.parse(
@@ -122,7 +147,13 @@ const cwd = process.cwd()
122147
JSON.stringify(nextPkg, null, 2)
123148
)
124149
// lerna publish in next step will fail if git status is not clean
125-
execSync('git update-index --skip-worktree packages/next/package.json')
150+
await execa(
151+
'git',
152+
['update-index', '--skip-worktree', 'packages/next/package.json'],
153+
{
154+
stdio: 'inherit',
155+
}
156+
)
126157
} catch (err) {
127158
console.error(err)
128159
process.exit(1)

scripts/publish-release.js

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@
22
// @ts-check
33

44
const path = require('path')
5-
const { readdir } = require('fs/promises')
5+
const execa = require('execa')
6+
const { Sema } = require('async-sema')
67
const { execSync } = require('child_process')
7-
const { readJson } = require('fs-extra')
8+
const { readJson, readdir } = require('fs-extra')
89

910
const cwd = process.cwd()
1011

1112
;(async function () {
1213
let isCanary = true
1314

14-
if (!process.env.NPM_TOKEN) {
15-
console.log('No NPM_TOKEN, exiting...')
16-
return
17-
}
18-
1915
try {
20-
const tagOutput = execSync('git describe --exact-match').toString()
16+
const tagOutput = execSync(
17+
`node ${path.join(__dirname, 'check-is-release.js')}`
18+
).toString()
2119
console.log(tagOutput)
2220

2321
if (tagOutput.trim().startsWith('v')) {
@@ -34,15 +32,28 @@ const cwd = process.cwd()
3432
}
3533
console.log(`Publishing ${isCanary ? 'canary' : 'stable'}`)
3634

35+
if (!process.env.NPM_TOKEN) {
36+
console.log('No NPM_TOKEN, exiting...')
37+
return
38+
}
39+
3740
const packagesDir = path.join(cwd, 'packages')
3841
const packageDirs = await readdir(packagesDir)
42+
const publishSema = new Sema(2)
3943

4044
const publish = async (pkg, retry = 0) => {
4145
try {
42-
execSync(
43-
`npm publish ${path.join(packagesDir, pkg)} --access public${
44-
isCanary ? ' --tag canary' : ''
45-
}`
46+
await publishSema.acquire()
47+
await execa(
48+
`npm`,
49+
[
50+
'publish',
51+
`${path.join(packagesDir, pkg)}`,
52+
'--access',
53+
'public',
54+
...(isCanary ? ['--tag', 'canary'] : []),
55+
],
56+
{ stdio: 'inherit' }
4657
)
4758
} catch (err) {
4859
console.error(`Failed to publish ${pkg}`, err)
@@ -66,18 +77,22 @@ const cwd = process.cwd()
6677
await publish(pkg, retry + 1)
6778
}
6879
throw err
80+
} finally {
81+
publishSema.release()
6982
}
7083
}
7184

72-
for (const packageDir of packageDirs) {
73-
const pkgJson = await readJson(
74-
path.join(packagesDir, packageDir, 'package.json')
75-
)
85+
await Promise.all(
86+
packageDirs.map(async (packageDir) => {
87+
const pkgJson = await readJson(
88+
path.join(packagesDir, packageDir, 'package.json')
89+
)
7690

77-
if (pkgJson.private) {
78-
console.log(`Skipping private package ${packageDir}`)
79-
continue
80-
}
81-
await publish(packageDir)
82-
}
91+
if (pkgJson.private) {
92+
console.log(`Skipping private package ${packageDir}`)
93+
return
94+
}
95+
await publish(packageDir)
96+
})
97+
)
8398
})()

0 commit comments

Comments
 (0)