mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
[16.2.x] Release pipeline updates (#95160)
Co-authored-by: Steven <[email protected]>
This commit is contained in:
co-authored by
Steven
parent
98941fc427
commit
534d9c144c
@@ -3,9 +3,13 @@ name: build-and-deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
# Don't run when tags or graphite base branches are pushed
|
||||
# Don't run when graphite base branches are pushed
|
||||
branches-ignore:
|
||||
- 'graphite-base/**'
|
||||
# Run on release tags so publishing is driven by the tag push rather than
|
||||
# the release-commit branch push (see the deploy-target step).
|
||||
tags:
|
||||
- 'v*'
|
||||
# we need the preview tarball for deploy tests
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
@@ -13,8 +17,11 @@ on:
|
||||
|
||||
concurrency:
|
||||
# Limit concurrent runs to 1 per PR,
|
||||
# but allow concurrent runs on push if they potentially use different source code
|
||||
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-{1}', github.workflow, github.ref_name) || format('{0}-sha-{1}', github.workflow, github.sha) }}
|
||||
# but allow concurrent runs on push if they potentially use different source code.
|
||||
# The push group includes github.ref so that the branch push and tag push of
|
||||
# the same release commit (same sha) don't share a group -- otherwise the
|
||||
# skipped branch run could cancel the production tag run.
|
||||
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-{1}', github.workflow, github.ref_name) || format('{0}-{1}-{2}', github.workflow, github.ref, github.sha) }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
@@ -66,26 +73,34 @@ jobs:
|
||||
|
||||
if [[ "$RELEASE_VERSION" == v* ]];
|
||||
then
|
||||
echo "value=production" >> $GITHUB_OUTPUT
|
||||
if [[ "$RELEASE_VERSION" == *-canary.* ]];
|
||||
if [[ "$GIT_REF" != refs/tags/* ]];
|
||||
then
|
||||
RELEASE_ENVIRONMENT="release-canary"
|
||||
elif [[ "$RELEASE_VERSION" == *-beta.* ]];
|
||||
then
|
||||
RELEASE_ENVIRONMENT="release-beta"
|
||||
elif [[ "$RELEASE_VERSION" == *-rc.* ]];
|
||||
then
|
||||
RELEASE_ENVIRONMENT="release-release-candidate"
|
||||
elif [[ "$RELEASE_VERSION" != *-* ]];
|
||||
then
|
||||
RELEASE_ENVIRONMENT="release-stable"
|
||||
# The release commit reached a branch (e.g. its push to canary).
|
||||
# Publishing is driven by the tag push for that same commit, so
|
||||
# skip here to avoid a duplicate production build on the same sha.
|
||||
echo "value=skipped" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "value=production" >> $GITHUB_OUTPUT
|
||||
if [[ "$RELEASE_VERSION" == *-canary.* ]];
|
||||
then
|
||||
RELEASE_ENVIRONMENT="release-canary"
|
||||
elif [[ "$RELEASE_VERSION" == *-beta.* ]];
|
||||
then
|
||||
RELEASE_ENVIRONMENT="release-beta"
|
||||
elif [[ "$RELEASE_VERSION" == *-rc.* ]];
|
||||
then
|
||||
RELEASE_ENVIRONMENT="release-release-candidate"
|
||||
elif [[ "$RELEASE_VERSION" != *-* ]];
|
||||
then
|
||||
RELEASE_ENVIRONMENT="release-stable"
|
||||
fi
|
||||
if [[ -z "$RELEASE_ENVIRONMENT" ]];
|
||||
then
|
||||
echo "::error::Missing release environment for $RELEASE_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
echo "release_environment=$RELEASE_ENVIRONMENT" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
if [[ -z "$RELEASE_ENVIRONMENT" ]];
|
||||
then
|
||||
echo "::error::Missing release environment for $RELEASE_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
echo "release_environment=$RELEASE_ENVIRONMENT" >> $GITHUB_OUTPUT
|
||||
elif [ ''"${GIT_REF}"'' == 'refs/heads/next-16-2' ]
|
||||
then
|
||||
echo "value=staging" >> $GITHUB_OUTPUT
|
||||
@@ -100,8 +115,10 @@ jobs:
|
||||
fi
|
||||
env:
|
||||
GIT_REF: ${{ github.ref }}
|
||||
- name: Print deploy target
|
||||
run: echo "Deploy target is '${{ steps.deploy-target.outputs.value }}'"
|
||||
- name: Print deploy plan
|
||||
run: |
|
||||
echo "Deploy target is '${{ steps.deploy-target.outputs.value }}'"
|
||||
echo "Release environment is '${{ steps.deploy-target.outputs.release_environment }}'"
|
||||
|
||||
build:
|
||||
if: ${{ needs.deploy-target.outputs.value != 'skipped' }}
|
||||
@@ -637,10 +654,18 @@ jobs:
|
||||
repositories: next.js
|
||||
permission-contents: write
|
||||
|
||||
- run: ./scripts/publish-native.js
|
||||
- run: ./scripts/publish-release.js
|
||||
env:
|
||||
RELEASE_GITHUB_TOKEN: ${{ steps.release-app-token.outputs.token }}
|
||||
BAIL_ON_NATIVE_WASM_PUBLISH_FAILURE: ${{ vars.BAIL_ON_NATIVE_WASM_PUBLISH_FAILURE }}
|
||||
|
||||
- name: Upload pnpm publish summary
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: pnpm-publish-summary
|
||||
path: pnpm-publish-summary.json
|
||||
if-no-files-found: ignore
|
||||
|
||||
publish-turbopack-npm-packages:
|
||||
# Matches the commit message written by turbopack/xtask/src/publish.rs:377
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
run: echo "LATEST_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: Check if new commits since last tag
|
||||
if: ${{ github.event.inputs.releaseType != 'stable' && github.event.inputs.force != true }}
|
||||
if: ${{ github.event.inputs.releaseType != 'stable' && inputs.force != true }}
|
||||
run: |
|
||||
if [ "$LATEST_TAG_COMMIT" = "$LATEST_COMMIT" ]; then
|
||||
echo "No new commits. Exiting..."
|
||||
|
||||
@@ -33,6 +33,6 @@ The type definitions are generated using the TypeScript [`tsc`](https://www.type
|
||||
|
||||
If you are actively working on Rust code or you need to test out the most recent Rust code that hasn't been published as a canary yet, you can [install Rust](https://www.rust-lang.org/tools/install) and run `pnpm swc-build-native`.
|
||||
|
||||
If you want to test out the wasm build locally, you will need to [install wasm-pack](https://rustwasm.github.io/wasm-pack/installer/). Run `pnpm --filter=@next/swc build-wasm --target <wasm_target>` to build and `node ./scripts/setup-wasm.mjs` to copy it into your `node_modules`. Run next with `NODE_OPTIONS='--no-addons'` to force it to use the wasm binary.
|
||||
If you want to test out the wasm build locally, you will need to [install wasm-pack](https://wasm-bindgen.github.io/wasm-pack/installer/). Run `pnpm --filter=@next/swc build-wasm --target <wasm_target>` to build and `node ./scripts/setup-wasm.mjs` to copy it into your `node_modules`. Run next with `NODE_OPTIONS='--no-addons'` to force it to use the wasm binary.
|
||||
|
||||
If you need to clean the project for any reason, use `pnpm clean`.
|
||||
|
||||
@@ -86,7 +86,6 @@
|
||||
"types": "lerna run types --stream",
|
||||
"types:test-lib": "tsc --project test/lib/tsconfig.json",
|
||||
"check-precompiled": "./scripts/check-pre-compiled.sh",
|
||||
"prepublishOnly": "turbo run build",
|
||||
"lint-staged": "lint-staged",
|
||||
"next-with-deps": "./scripts/next-with-deps.sh",
|
||||
"next": "cross-env NEXT_PRIVATE_LOCAL_DEV=1 NEXT_TELEMETRY_DISABLED=1 NODE_OPTIONS=\"--trace-deprecation --enable-source-maps\" next",
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
"dev": "ncc build ./index.ts -w -o dist/",
|
||||
"prebuild": "node ../../scripts/rm.mjs dist",
|
||||
"build": "ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
|
||||
"prepublishOnly": "cd ../../ && turbo run build",
|
||||
"lint-fix": "pnpm prettier -w --plugin prettier-plugin-tailwindcss 'templates/*-tw/{ts,js}/{app,pages}/**/*.{js,ts,tsx}'"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -39,8 +39,7 @@
|
||||
"scripts": {
|
||||
"dev": "pnpm build",
|
||||
"build": "swc -d dist src && pnpm types",
|
||||
"types": "tsc --skipLibCheck --declaration --emitDeclarationOnly --esModuleInterop --declarationDir dist --rootDir src",
|
||||
"prepublishOnly": "cd ../../ && turbo run build"
|
||||
"types": "tsc --skipLibCheck --declaration --emitDeclarationOnly --esModuleInterop --declarationDir dist --rootDir src"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
"scripts": {
|
||||
"dev": "pnpm build",
|
||||
"build": "swc -d dist src && pnpm types",
|
||||
"types": "tsc src/index.ts --skipLibCheck --declaration --emitDeclarationOnly --esModuleInterop --declarationDir dist",
|
||||
"prepublishOnly": "cd ../../ && turbo run build"
|
||||
"types": "tsc src/index.ts --skipLibCheck --declaration --emitDeclarationOnly --esModuleInterop --declarationDir dist"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "node ../../scripts/rm.mjs dist && pnpm ncc-fontkit && tsc -d -p tsconfig.build.json",
|
||||
"prepublishOnly": "cd ../../ && turbo run build",
|
||||
"dev": "pnpm ncc-fontkit && tsc -d -w -p tsconfig.json",
|
||||
"typescript": "tsec --noEmit -p tsconfig.json",
|
||||
"ncc-fontkit": "ncc build ./fontkit.js --minify -o dist/fontkit"
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm tsc -d -p tsconfig.json",
|
||||
"prepublishOnly": "cd ../../ && turbo run build",
|
||||
"dev": "pnpm tsc -d -w -p tsconfig.json",
|
||||
"test": "jest",
|
||||
"test:upgrade-fixture": "./scripts/test-upgrade-fixture.sh"
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
"prebuild:source": "node ../../scripts/rm.mjs dist",
|
||||
"types": "tsc --declaration --emitDeclarationOnly --declarationDir dist",
|
||||
"build:source": "ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
|
||||
"build": "pnpm build:source && pnpm types",
|
||||
"prepublishOnly": "cd ../../ && turbo run build"
|
||||
"build": "pnpm build:source && pnpm types"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "0.34.0",
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "node ../../scripts/rm.mjs dist && tsc -d -p tsconfig.json",
|
||||
"prepublishOnly": "cd ../../ && turbo run build",
|
||||
"dev": "tsc -d -w -p tsconfig.json",
|
||||
"typescript": "tsec --noEmit -p tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "microbundle -i src/index.js -o dist/polyfill-module.js -f iife --no-sourcemap --external none --no-pkg-main",
|
||||
"dev": "pnpm build",
|
||||
"prepublishOnly": "cd ../../ && turbo run build"
|
||||
"dev": "pnpm build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"microbundle": "0.15.0"
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "microbundle -i src/index.js -o dist/polyfill-nomodule.js -f iife --no-sourcemap --external none --no-pkg-main",
|
||||
"dev": "pnpm build",
|
||||
"prepublishOnly": "cd ../../ && turbo run build"
|
||||
"dev": "pnpm build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"core-js": "3.38.1",
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
"release": "ncc build ./src/index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
|
||||
"build": "pnpm release && pnpm types",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"prepublishOnly": "cd ../../ && turbo run build"
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.0",
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
"scripts": {
|
||||
"dev": "cross-env NEXT_SERVER_NO_MANGLE=1 taskr",
|
||||
"build": "taskr release",
|
||||
"prepublishOnly": "cd ../../ && turbo run build",
|
||||
"types": "tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --stripInternal --declarationDir dist",
|
||||
"typescript": "tsec --noEmit",
|
||||
"ncc-compiled": "taskr ncc",
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "node ../../scripts/rm.mjs dist && tsc -d -p tsconfig.json",
|
||||
"prepublishOnly": "cd ../../ && turbo run build",
|
||||
"dev": "tsc -d -w -p tsconfig.json"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "node ../../scripts/rm.mjs dist && tsc -d -p tsconfig.json",
|
||||
"prepublishOnly": "cd ../../ && turbo run build",
|
||||
"dev": "tsc -d -w -p tsconfig.json",
|
||||
"typescript": "tsec --noEmit -p tsconfig.json"
|
||||
},
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const { Sema } = require('async-sema')
|
||||
const { readFile, readdir, writeFile, cp } = require('fs/promises')
|
||||
|
||||
const cwd = process.cwd()
|
||||
|
||||
;(async function () {
|
||||
try {
|
||||
const publishSema = new Sema(2)
|
||||
|
||||
let version = require('@next/swc/package.json').version
|
||||
|
||||
// Copy binaries to package folders, update version, and publish
|
||||
let nativePackagesDir = path.join(cwd, 'crates/next-napi-bindings/npm')
|
||||
let platforms = (await readdir(nativePackagesDir)).filter(
|
||||
(name) => !name.startsWith('.')
|
||||
)
|
||||
|
||||
await Promise.all(
|
||||
platforms.map(async (platform) => {
|
||||
await publishSema.acquire()
|
||||
let output = ''
|
||||
|
||||
try {
|
||||
let binaryName = `next-swc.${platform}.node`
|
||||
await cp(
|
||||
path.join(cwd, 'packages/next-swc/native', binaryName),
|
||||
path.join(nativePackagesDir, platform, binaryName)
|
||||
)
|
||||
let pkg = JSON.parse(
|
||||
await readFile(
|
||||
path.join(nativePackagesDir, platform, 'package.json')
|
||||
)
|
||||
)
|
||||
pkg.version = version
|
||||
await writeFile(
|
||||
path.join(nativePackagesDir, platform, 'package.json'),
|
||||
JSON.stringify(pkg, null, 2)
|
||||
)
|
||||
const child = execa(
|
||||
`npm`,
|
||||
[
|
||||
`publish`,
|
||||
`${path.join(nativePackagesDir, platform)}`,
|
||||
`--access`,
|
||||
`public`,
|
||||
...(version.includes('canary') ? ['--tag', 'canary'] : []),
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
)
|
||||
const handleData = (type) => (chunk) => {
|
||||
process[type].write(chunk)
|
||||
output += chunk.toString()
|
||||
}
|
||||
child.stdout?.on('data', handleData('stdout'))
|
||||
child.stderr?.on('data', handleData('stderr'))
|
||||
await child
|
||||
} catch (err) {
|
||||
// don't block publishing other versions on single platform error
|
||||
console.error(`Failed to publish`, platform, err)
|
||||
|
||||
if (
|
||||
output.includes(
|
||||
'cannot publish over the previously published versions'
|
||||
)
|
||||
) {
|
||||
console.error('Ignoring already published error', platform, err)
|
||||
} else {
|
||||
// throw err
|
||||
}
|
||||
} finally {
|
||||
publishSema.release()
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
// Update name/version of wasm packages and publish
|
||||
const pkgDirectory = 'crates/wasm'
|
||||
let wasmDir = path.join(cwd, pkgDirectory)
|
||||
await Promise.all(
|
||||
['web', 'nodejs'].map(async (wasmTarget) => {
|
||||
await publishSema.acquire()
|
||||
let wasmPkg = JSON.parse(
|
||||
await readFile(path.join(wasmDir, `pkg-${wasmTarget}/package.json`))
|
||||
)
|
||||
wasmPkg.name = `@next/swc-wasm-${wasmTarget}`
|
||||
wasmPkg.version = version
|
||||
wasmPkg.repository = {
|
||||
type: 'git',
|
||||
url: 'https://github.com/vercel/next.js',
|
||||
directory: pkgDirectory,
|
||||
}
|
||||
await writeFile(
|
||||
path.join(wasmDir, `pkg-${wasmTarget}/package.json`),
|
||||
JSON.stringify(wasmPkg, null, 2)
|
||||
)
|
||||
try {
|
||||
await execa(
|
||||
`npm`,
|
||||
[
|
||||
'publish',
|
||||
`${path.join(wasmDir, `pkg-${wasmTarget}`)}`,
|
||||
'--access',
|
||||
'public',
|
||||
...(version.includes('canary') ? ['--tag', 'canary'] : []),
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
)
|
||||
} catch (err) {
|
||||
// don't block publishing other versions on single platform error
|
||||
console.error(`Failed to publish`, wasmTarget, err)
|
||||
if (
|
||||
err.message &&
|
||||
err.message.includes(
|
||||
'You cannot publish over the previously published versions'
|
||||
)
|
||||
) {
|
||||
console.error('Ignoring already published error', wasmTarget)
|
||||
} else {
|
||||
// throw err
|
||||
}
|
||||
} finally {
|
||||
publishSema.release()
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
// Update optional dependencies versions
|
||||
let nextPkg = JSON.parse(
|
||||
await readFile(path.join(cwd, 'packages/next/package.json'))
|
||||
)
|
||||
for (let platform of platforms) {
|
||||
let optionalDependencies = nextPkg.optionalDependencies || {}
|
||||
optionalDependencies['@next/swc-' + platform] = version
|
||||
nextPkg.optionalDependencies = optionalDependencies
|
||||
}
|
||||
await writeFile(
|
||||
path.join(path.join(cwd, 'packages/next/package.json')),
|
||||
JSON.stringify(nextPkg, null, 2)
|
||||
)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
}
|
||||
})()
|
||||
+235
-144
@@ -5,55 +5,38 @@ const path = require('path')
|
||||
const execa = require('execa')
|
||||
const semver = require('semver')
|
||||
const { Sema } = require('async-sema')
|
||||
const { execSync } = require('child_process')
|
||||
const fs = require('fs')
|
||||
const fs = require('fs/promises')
|
||||
const {
|
||||
getGitHubToken,
|
||||
getGitHubTokenMissingMessage,
|
||||
} = require('./release-github-auth')
|
||||
|
||||
const cwd = process.cwd()
|
||||
const dryRun = process.argv.includes('--dry-run')
|
||||
const maxPublishAttempts = 4
|
||||
const publishRetryDelaySeconds = 15
|
||||
|
||||
;(async function () {
|
||||
let isCanary = true
|
||||
let isReleaseCandidate = false
|
||||
let isBeta = false
|
||||
|
||||
try {
|
||||
const tagOutput = execSync(
|
||||
`node ${path.join(__dirname, 'check-is-release.js')}`
|
||||
).toString()
|
||||
console.log(tagOutput)
|
||||
|
||||
if (tagOutput.trim().startsWith('v')) {
|
||||
isCanary = tagOutput.includes('-canary')
|
||||
}
|
||||
isReleaseCandidate = tagOutput.includes('-rc')
|
||||
isBeta = tagOutput.includes('-beta')
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
|
||||
if (err.message && err.message.includes('no tag exactly matches')) {
|
||||
console.log('Nothing to publish, exiting...')
|
||||
return
|
||||
}
|
||||
throw err
|
||||
if (dryRun) {
|
||||
console.log('Dry run: not publishing to npm')
|
||||
}
|
||||
const publishSema = new Sema(2)
|
||||
|
||||
let tag = isCanary
|
||||
? 'canary'
|
||||
: isReleaseCandidate
|
||||
? 'rc'
|
||||
: isBeta
|
||||
? 'beta'
|
||||
: 'latest'
|
||||
const { version } = JSON.parse(
|
||||
await fs.readFile(path.join(cwd, 'lerna.json'), 'utf-8')
|
||||
)
|
||||
const parsedVersion = semver.parse(version)
|
||||
if (parsedVersion === null) {
|
||||
throw new Error(`Invalid version in lerna.json: ${version}`)
|
||||
}
|
||||
const prereleaseChannel = parsedVersion.prerelease[0]
|
||||
const isPrerelease = prereleaseChannel != null
|
||||
console.log(`Publishing ${version}`)
|
||||
|
||||
let npmDistTag = isPrerelease ? String(prereleaseChannel) : 'latest'
|
||||
|
||||
try {
|
||||
if (!isCanary && !isReleaseCandidate && !isBeta) {
|
||||
const version = JSON.parse(
|
||||
await fs.promises.readFile(path.join(cwd, 'lerna.json'), 'utf-8')
|
||||
).version
|
||||
|
||||
if (!isPrerelease) {
|
||||
const res = await fetch(
|
||||
`https://registry.npmjs.org/-/package/next/dist-tags`
|
||||
)
|
||||
@@ -67,7 +50,7 @@ const cwd = process.cwd()
|
||||
// Therefore, we explicitly set the tag as 'backport' for backports.
|
||||
// But force @latest tag if we accidentally tagged a prerelase as latest
|
||||
if (!semver.prerelease(tags.latest)) {
|
||||
tag = 'backport'
|
||||
npmDistTag = 'backport'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,29 +59,13 @@ const cwd = process.cwd()
|
||||
throw error
|
||||
}
|
||||
|
||||
console.log(`Publishing as "${tag}" dist tag...`)
|
||||
console.log(`Publishing as "${npmDistTag}" dist tag...`)
|
||||
|
||||
const packagesDir = path.join(cwd, 'packages')
|
||||
const packageDirs = fs.readdirSync(packagesDir)
|
||||
const publishSema = new Sema(2)
|
||||
|
||||
const publish = async (pkg, retry = 0) => {
|
||||
const publish = async (label, args, attempt = 1) => {
|
||||
let output = ''
|
||||
try {
|
||||
await publishSema.acquire()
|
||||
const child = execa(
|
||||
`npm`,
|
||||
[
|
||||
'publish',
|
||||
`${path.join(packagesDir, pkg)}`,
|
||||
'--access',
|
||||
'public',
|
||||
'--ignore-scripts',
|
||||
'--tag',
|
||||
tag,
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
)
|
||||
const child = execa('pnpm', args, { stdio: 'pipe' })
|
||||
const handleData = (type) => (chunk) => {
|
||||
process[type].write(chunk)
|
||||
output += chunk.toString()
|
||||
@@ -108,119 +75,243 @@ const cwd = process.cwd()
|
||||
// Return here to avoid retry logic
|
||||
return await child
|
||||
} catch (err) {
|
||||
console.error(`Failed to publish ${pkg}`, err)
|
||||
console.error(
|
||||
`Failed to publish ${label} (attempt ${attempt} of ${maxPublishAttempts})`,
|
||||
err
|
||||
)
|
||||
|
||||
if (
|
||||
output.includes('cannot publish over the previously published versions')
|
||||
) {
|
||||
console.error('Ignoring already published error', pkg)
|
||||
console.error('Ignoring already published error', label)
|
||||
return
|
||||
}
|
||||
|
||||
if (retry >= 3) {
|
||||
if (attempt >= maxPublishAttempts) {
|
||||
throw err
|
||||
}
|
||||
} finally {
|
||||
publishSema.release()
|
||||
}
|
||||
// Recursive call need to be outside of the publishSema
|
||||
const retryDelaySeconds = 15
|
||||
console.log(`retrying in ${retryDelaySeconds}s`)
|
||||
console.log(`retrying ${label} in ${publishRetryDelaySeconds}s`)
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, retryDelaySeconds * 1000)
|
||||
setTimeout(resolve, publishRetryDelaySeconds * 1000)
|
||||
)
|
||||
await publish(pkg, retry + 1)
|
||||
await publish(label, args, attempt + 1)
|
||||
}
|
||||
|
||||
const undraft = async () => {
|
||||
const githubToken = getGitHubToken()
|
||||
// Copy binaries to package folders, update version, and publish
|
||||
const nativePackagesDir = path.join(cwd, 'crates/next-napi-bindings/npm')
|
||||
const platforms = (await fs.readdir(nativePackagesDir)).filter(
|
||||
(name) => !name.startsWith('.')
|
||||
)
|
||||
|
||||
if (!githubToken) {
|
||||
throw new Error(getGitHubTokenMissingMessage())
|
||||
}
|
||||
|
||||
if (isCanary) {
|
||||
const nativeResults = await Promise.allSettled(
|
||||
platforms.map(async (platform) => {
|
||||
const binaryName = `next-swc.${platform}.node`
|
||||
try {
|
||||
const ghHeaders = {
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${githubToken}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}
|
||||
const { version: _version } = require('../lerna.json')
|
||||
const version = `v${_version}`
|
||||
|
||||
let release
|
||||
let releasesData
|
||||
|
||||
// The release might take a minute to show up in
|
||||
// the list so retry a bit
|
||||
for (let i = 0; i < 6; i++) {
|
||||
try {
|
||||
const releaseUrlRes = await fetch(
|
||||
`https://api.github.com/repos/vercel/next.js/releases`,
|
||||
{
|
||||
headers: ghHeaders,
|
||||
}
|
||||
)
|
||||
releasesData = await releaseUrlRes.json()
|
||||
|
||||
release = releasesData.find(
|
||||
(release) => release.tag_name === version
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(`Fetching release failed`, err)
|
||||
}
|
||||
if (!release) {
|
||||
console.log(`Retrying in 10s...`)
|
||||
await new Promise((resolve) => setTimeout(resolve, 10 * 1000))
|
||||
}
|
||||
}
|
||||
|
||||
if (!release) {
|
||||
console.log(`Failed to find release`, releasesData)
|
||||
await fs.cp(
|
||||
path.join(cwd, 'packages/next-swc/native', binaryName),
|
||||
path.join(nativePackagesDir, platform, binaryName)
|
||||
)
|
||||
} catch (error) {
|
||||
if (dryRun) {
|
||||
console.warn(
|
||||
`Binary ${binaryName} not found, but ignoring due to dry run`
|
||||
)
|
||||
return
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
const undraftRes = await fetch(release.url, {
|
||||
headers: ghHeaders,
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
draft: false,
|
||||
name: version,
|
||||
}),
|
||||
const pkgDir = path.join(nativePackagesDir, platform)
|
||||
const pkg = JSON.parse(
|
||||
await fs.readFile(path.join(pkgDir, 'package.json'), {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
|
||||
if (undraftRes.ok) {
|
||||
console.log('un-drafted canary release successfully')
|
||||
} else {
|
||||
console.log(`Failed to undraft`, await undraftRes.text())
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Failed to undraft release`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const results = await Promise.allSettled(
|
||||
packageDirs.map(async (packageDir) => {
|
||||
const pkgJson = JSON.parse(
|
||||
await fs.promises.readFile(
|
||||
path.join(packagesDir, packageDir, 'package.json'),
|
||||
'utf-8'
|
||||
)
|
||||
)
|
||||
|
||||
if (pkgJson.private) {
|
||||
console.log(`Skipping private package ${packageDir}`)
|
||||
return
|
||||
}
|
||||
await publish(packageDir)
|
||||
pkg.version = version
|
||||
await fs.writeFile(
|
||||
path.join(pkgDir, 'package.json'),
|
||||
JSON.stringify(pkg, null, 2)
|
||||
)
|
||||
await publish(platform, [
|
||||
'publish',
|
||||
pkgDir,
|
||||
'--access',
|
||||
'public',
|
||||
'--no-git-checks',
|
||||
'--ignore-scripts',
|
||||
'--tag',
|
||||
npmDistTag,
|
||||
...(dryRun ? ['--dry-run'] : []),
|
||||
])
|
||||
})
|
||||
)
|
||||
|
||||
if (results.some((item) => item.status === 'rejected')) {
|
||||
console.error(`Not all packages published successfully`, results)
|
||||
// Update name/version of wasm packages and publish
|
||||
const pkgDirectory = 'crates/wasm'
|
||||
const wasmDir = path.join(cwd, pkgDirectory)
|
||||
const wasmResults = await Promise.allSettled(
|
||||
['web', 'nodejs'].map(async (wasmTarget) => {
|
||||
const pkgDir = path.join(wasmDir, `pkg-${wasmTarget}`)
|
||||
const wasmPkg = JSON.parse(
|
||||
await fs.readFile(path.join(pkgDir, 'package.json'), {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
)
|
||||
wasmPkg.name = `@next/swc-wasm-${wasmTarget}`
|
||||
wasmPkg.version = version
|
||||
wasmPkg.repository = {
|
||||
type: 'git',
|
||||
url: 'https://github.com/vercel/next.js',
|
||||
directory: pkgDirectory,
|
||||
}
|
||||
await fs.writeFile(
|
||||
path.join(pkgDir, 'package.json'),
|
||||
JSON.stringify(wasmPkg, null, 2)
|
||||
)
|
||||
await publish(`wasm-${wasmTarget}`, [
|
||||
'publish',
|
||||
pkgDir,
|
||||
'--access',
|
||||
'public',
|
||||
'--no-git-checks',
|
||||
'--ignore-scripts',
|
||||
'--tag',
|
||||
npmDistTag,
|
||||
...(dryRun ? ['--dry-run'] : []),
|
||||
])
|
||||
})
|
||||
)
|
||||
|
||||
if (nativeResults.some((item) => item.status === 'rejected')) {
|
||||
console.error(
|
||||
`Not all native packages published successfully`,
|
||||
nativeResults
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
await undraft()
|
||||
})()
|
||||
|
||||
if (wasmResults.some((item) => item.status === 'rejected')) {
|
||||
console.error(`Not all wasm packages published successfully`, wasmResults)
|
||||
if (process.env.BAIL_ON_NATIVE_WASM_PUBLISH_FAILURE === 'true') {
|
||||
process.exit(1)
|
||||
} else {
|
||||
console.warn(
|
||||
'Continuing with release even though some wasm packages failed to publish because BAIL_ON_NATIVE_WASM_PUBLISH_FAILURE is not set to true'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Update optional dependencies versions
|
||||
const nextPkg = JSON.parse(
|
||||
await fs.readFile(path.join(cwd, 'packages/next/package.json'), {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
)
|
||||
for (const platform of platforms) {
|
||||
const optionalDependencies = nextPkg.optionalDependencies || {}
|
||||
optionalDependencies['@next/swc-' + platform] = version
|
||||
nextPkg.optionalDependencies = optionalDependencies
|
||||
}
|
||||
// These props are only needed for development and build so we strip
|
||||
// before publishing to reduce the total metadata size. The max is
|
||||
// 100 MB for all versions of a pkg and we are currently at 30MB. Run:
|
||||
// curl -w '%{size_download}' -so /dev/null https://registry.npmjs.org/next | awk '{print $1/1000000 " MB"}'
|
||||
for (const field of ['devDependencies', 'taskr', 'scripts']) {
|
||||
delete nextPkg[field]
|
||||
}
|
||||
await fs.writeFile(
|
||||
path.join(cwd, 'packages/next/package.json'),
|
||||
JSON.stringify(nextPkg, null, 2)
|
||||
)
|
||||
|
||||
await publish('workspace', [
|
||||
'--filter',
|
||||
'./packages/**',
|
||||
'publish',
|
||||
'--recursive',
|
||||
'--access',
|
||||
'public',
|
||||
'--no-git-checks',
|
||||
'--ignore-scripts',
|
||||
'--report-summary',
|
||||
'--tag',
|
||||
npmDistTag,
|
||||
...(dryRun ? ['--dry-run'] : []),
|
||||
])
|
||||
|
||||
if (dryRun) {
|
||||
console.log('Dry run: skipping GitHub release un-draft')
|
||||
return
|
||||
}
|
||||
|
||||
const githubToken = getGitHubToken()
|
||||
|
||||
if (!githubToken) {
|
||||
throw new Error(getGitHubTokenMissingMessage())
|
||||
}
|
||||
|
||||
if (isPrerelease) {
|
||||
try {
|
||||
const ghHeaders = {
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${githubToken}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}
|
||||
const tag = `v${version}`
|
||||
|
||||
let release
|
||||
let releasesData
|
||||
|
||||
// The release might take a minute to show up in
|
||||
// the list so retry a bit
|
||||
for (let i = 0; i < 6; i++) {
|
||||
try {
|
||||
const releaseUrlRes = await fetch(
|
||||
`https://api.github.com/repos/vercel/next.js/releases`,
|
||||
{
|
||||
headers: ghHeaders,
|
||||
}
|
||||
)
|
||||
releasesData = await releaseUrlRes.json()
|
||||
|
||||
release = releasesData.find((release) => release.tag_name === tag)
|
||||
} catch (err) {
|
||||
console.log(`Fetching release failed`, err)
|
||||
}
|
||||
if (!release) {
|
||||
console.log(`Retrying in 10s...`)
|
||||
await new Promise((resolve) => setTimeout(resolve, 10 * 1000))
|
||||
}
|
||||
}
|
||||
|
||||
if (!release) {
|
||||
console.log(`Failed to find release`, releasesData)
|
||||
return
|
||||
}
|
||||
|
||||
const undraftRes = await fetch(release.url, {
|
||||
headers: ghHeaders,
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
draft: false,
|
||||
name: tag,
|
||||
}),
|
||||
})
|
||||
|
||||
if (undraftRes.ok) {
|
||||
console.log(`un-drafted ${prereleaseChannel} release successfully`)
|
||||
} else {
|
||||
console.log(`Failed to undraft`, await undraftRes.text())
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Failed to undraft release`, err)
|
||||
}
|
||||
}
|
||||
})().catch((err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
+48
-29
@@ -19,6 +19,7 @@ async function main() {
|
||||
const isCanary = releaseType === 'canary'
|
||||
const isReleaseCandidate = releaseType === 'release-candidate'
|
||||
const isBeta = releaseType === 'beta'
|
||||
const dryRun = args.includes('--dry-run')
|
||||
|
||||
if (
|
||||
releaseType !== 'stable' &&
|
||||
@@ -42,27 +43,33 @@ async function main() {
|
||||
|
||||
const githubToken = getGitHubToken()
|
||||
|
||||
if (!githubToken) {
|
||||
console.log(getGitHubTokenMissingMessage())
|
||||
return
|
||||
if (dryRun) {
|
||||
console.log(
|
||||
'Dry run: keeping commits locally, skipping git push and GitHub release creation'
|
||||
)
|
||||
} else {
|
||||
if (!githubToken) {
|
||||
console.log(getGitHubTokenMissingMessage())
|
||||
return
|
||||
}
|
||||
|
||||
const configStorePath = resolveFrom(
|
||||
path.join(process.cwd(), 'node_modules/release'),
|
||||
'configstore'
|
||||
)
|
||||
const ConfigStore = require(configStorePath)
|
||||
|
||||
const config = new ConfigStore('release')
|
||||
config.set('token', githubToken)
|
||||
|
||||
await configureGitHubAuth(githubToken)
|
||||
await verifyGitHubApiAccess(
|
||||
githubToken,
|
||||
'/repos/vercel/next.js/releases?per_page=1',
|
||||
'release lookup'
|
||||
)
|
||||
}
|
||||
|
||||
const configStorePath = resolveFrom(
|
||||
path.join(process.cwd(), 'node_modules/release'),
|
||||
'configstore'
|
||||
)
|
||||
const ConfigStore = require(configStorePath)
|
||||
|
||||
const config = new ConfigStore('release')
|
||||
config.set('token', githubToken)
|
||||
|
||||
await configureGitHubAuth(githubToken)
|
||||
await verifyGitHubApiAccess(
|
||||
githubToken,
|
||||
'/repos/vercel/next.js/releases?per_page=1',
|
||||
'release lookup'
|
||||
)
|
||||
|
||||
console.log(`Running pnpm release-${isCanary ? 'canary' : 'stable'}...`)
|
||||
const preleaseType =
|
||||
semverType === 'major'
|
||||
@@ -87,24 +94,36 @@ async function main() {
|
||||
|
||||
lernaArgs.push('--force-publish', '-y', '--no-push')
|
||||
|
||||
if (dryRun) {
|
||||
// So the dry-run can be exercised outside
|
||||
// of the release branches lerna.json restricts in real publishes.
|
||||
lernaArgs.push('--allow-branch', '**')
|
||||
}
|
||||
|
||||
const child = execa('pnpm', lernaArgs, {
|
||||
stdio: 'inherit',
|
||||
})
|
||||
|
||||
await child
|
||||
|
||||
await createGitHubReleaseCommit(githubToken)
|
||||
|
||||
if (isCanary || isReleaseCandidate || isBeta) {
|
||||
const releaseChild = execa(
|
||||
'pnpm',
|
||||
['release', '--pre', '--skip-questions', '--show-url'],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
}
|
||||
if (dryRun) {
|
||||
console.log(
|
||||
'Dry run: skipping GitHub-signed release commit and GitHub release creation'
|
||||
)
|
||||
} else {
|
||||
await createGitHubReleaseCommit(githubToken)
|
||||
|
||||
await releaseChild
|
||||
if (isCanary || isReleaseCandidate || isBeta) {
|
||||
const releaseChild = execa(
|
||||
'pnpm',
|
||||
['release', '--pre', '--skip-questions', '--show-url'],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
}
|
||||
)
|
||||
|
||||
await releaseChild
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Release process is finished')
|
||||
|
||||
Reference in New Issue
Block a user