vercel/next.js
33 workflows · maturity 67% · 13 patterns · GitHub ↗
Practices
✓ Matrix✓ Permissions○ Security scan○ AI review✓ Cache✓ Concurrency✓ Reusable workflows
Detected patterns
Security dimensions
Workflows (33)
build_and_deploy matrix .github/workflows/build_and_deploy.yml
View raw YAML
# Update all mentions of this name in vercel-packages when changing.
name: build-and-deploy
on:
push:
# Don't run when tags or graphite base branches are pushed
branches-ignore:
- 'graphite-base/**'
# we need the preview tarball for deploy tests
pull_request:
types: [opened, synchronize]
workflow_dispatch:
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) }}
cancel-in-progress: true
env:
NAPI_CLI_VERSION: 2.18.4
TURBO_VERSION: 2.8.11
# --env-mode loose is a breaking change required with turbo 2.x since Strict mode is now the default
# TODO: we should add the relevant envs later to to switch to strict mode
TURBO_ARGS: '-v --env-mode loose --remote-cache-timeout 90 --summarize --log-order stream'
NODE_LTS_VERSION: 20
TURBO_TEAM: 'vercel'
TURBO_CACHE: 'remote:rw'
# Without this environment variable, rust-lld will fail because some dependencies defaults to newer version of macOS by default.
#
# See https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#os-version for more details
MACOSX_DEPLOYMENT_TARGET: 11.0
# Run GitHub Actions JS with Node.js 24 to avoid deprecation warnings
# (needed until actions/checkout, actions/setup-node, etc. all default to node24)
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
deploy-target:
runs-on: ubuntu-latest
# Don't trigger this job on `pull_request` events from upstream branches.
# Those would already run this job on the `push` event
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork }}
outputs:
value: ${{ steps.deploy-target.outputs.value }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- run: echo "${{ github.event.after }}"
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: Determine deploy target
# 'force-preview' performs a full preview build but only if acknowledged i.e. workflow_dispatch
# 'automated-preview' for pushes on branches other than 'canary' for integration testing.
# 'staging' for canary branch since that will eventually be published i.e. become the production build.
id: deploy-target
run: |
if [[ $(node ./scripts/check-is-release.js 2> /dev/null || :) == v* ]];
then
echo "value=production" >> $GITHUB_OUTPUT
elif [ '${{ github.ref }}' == 'refs/heads/canary' ]
then
echo "value=staging" >> $GITHUB_OUTPUT
elif [ '${{ github.event_name }}' == 'workflow_dispatch' ]
then
echo "value=force-preview" >> $GITHUB_OUTPUT
elif [[ $(node scripts/run-for-change.mjs --not --type docs --exec echo 'false') != 'false' ]];
then
echo "value=skipped" >> $GITHUB_OUTPUT
else
echo "value=automated-preview" >> $GITHUB_OUTPUT
fi
- name: Print deploy target
run: echo "Deploy target is '${{ steps.deploy-target.outputs.value }}'"
build:
if: ${{ needs.deploy-target.outputs.value != 'skipped' }}
needs:
- deploy-target
runs-on: ubuntu-latest
env:
NEXT_TELEMETRY_DISABLED: 1
# we build a dev binary for use in CI so skip downloading
# canary next-swc binaries in the monorepo
NEXT_SKIP_NATIVE_POSTINSTALL: 1
steps:
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- uses: actions/checkout@v6
with:
fetch-depth: 25
- id: get-store-path
run: echo STORE_PATH=$(pnpm store path) >> $GITHUB_OUTPUT
- uses: actions/cache@v5
timeout-minutes: 5
id: cache-pnpm-store
with:
path: ${{ steps.get-store-path.outputs.STORE_PATH }}
key: pnpm-store-v2-${{ hashFiles('pnpm-lock.yaml') }}
# Do not use restore-keys since it leads to indefinite growth of the cache.
- run: pnpm install
- name: Set preview version
if: ${{ contains(fromJSON('["automated-preview","force-preview"]'), needs.deploy-target.outputs.value) }}
run: |
node scripts/set-preview-version.js "${{ github.sha }}"
pnpm install --no-frozen-lockfile
- run: pnpm run build
- uses: actions/cache@v5
timeout-minutes: 5
id: cache-build
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt}}
# Generate the build matrix for native binaries.
# For automated-preview, only build linux/x86_64/gnu (the target we run automated tests against).
generate-native-matrix:
if: ${{ needs.deploy-target.outputs.value != 'skipped' }}
needs:
- deploy-target
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- uses: mmastrac/mmm-matrix@3edd85c30addba11887c770740309c979a446aa9 # v1
id: matrix
with:
config: |
deployTarget: ${{ needs.deploy-target.outputs.value }}
input: |
$if: "config.deployTarget != 'automated-preview' || (this.target == 'x86_64-unknown-linux-gnu')"
target:
$dynamic: "`${this.arch}-${this.vendor}-${this.sys}${this.abi ? '-' + this.abi : ''}`"
build_task: build-native-release
os:
mac:
host: "['self-hosted', 'macos', 'arm64']"
arch: [x86_64, aarch64]
vendor: apple
sys: darwin
windows:
host: "['self-hosted', 'windows', 'x64']"
vendor: pc
sys: windows
abi: msvc
arch:
x86_64: {}
aarch64:
build_task: build-native-no-plugin-release
linux:
host: "['self-hosted', 'linux', 'x64', 'metal']"
docker: next-swc-builder:latest
vendor: unknown
sys: linux
abi:
gnu:
arch: [x86_64, aarch64]
musl:
arch: [x86_64, aarch64]
# Build binaries for publishing
build-native:
if: ${{ needs.deploy-target.outputs.value != 'skipped' }}
needs:
- deploy-target
- generate-native-matrix
defaults:
run:
shell: bash -leo pipefail {0}
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.generate-native-matrix.outputs.matrix) }}
name: stable - ${{ matrix.target }} - node@20
runs-on: ${{ fromJSON(matrix.host) }}
timeout-minutes: 45
steps:
# Enable long paths on Windows to avoid MAX_PATH (260 char) errors
# with deeply nested node_modules/.pnpm paths
- name: Enable git long paths
if: ${{ matrix.os == 'windows' }}
run: git config --system core.longpaths true
# we use checkout here instead of the build cache since
# it can fail to restore in different OS'
- uses: actions/checkout@v6
with:
# crates/next-napi-bindings/build.rs uses git-describe to find the most recent git tag. It's okay if
# this fails, but fetch with enough depth that we're likely to find a recent tag.
fetch-depth: 100
- name: Setup node
uses: actions/setup-node@v6
if: ${{ !matrix.docker }}
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
# we always want to run this to set environment variables
# (skip for docker builds - rust is already in the image)
- name: Install Rust
if: ${{ !matrix.docker }}
uses: ./.github/actions/setup-rust
with:
targets: ${{ matrix.target }}
- name: normalize versions
run: node scripts/normalize-version-bump.js
- name: Cache on ${{ github.ref_name }}
if: ${{ !matrix.docker }}
uses: ijjk/rust-cache@turbo-cache-v1.0.9
with:
save-if: 'true'
cache-provider: 'turbo'
shared-key: build-${{ matrix.target }}-${{ hashFiles('.cargo/config.toml') }}
- name: Clear native build
run: rm -rf packages/next-swc/native
# Build or restore the Docker image via turbo-cache.js.
- name: Build/restore Docker image
if: ${{ matrix.docker }}
run: node scripts/docker-image-cache.js
# Try to restore previously-built native binary from turbo cache
- name: pull build cache
if: ${{ matrix.docker }}
run: TURBO_VERSION=${TURBO_VERSION} node ./scripts/pull-turbo-cache.js ${{ matrix.target }}
- name: check build exists
if: ${{ matrix.docker }}
run: if [ -f packages/next-swc/native/next-swc.*.node ]; then echo "BUILD_EXISTS=yes" >> $GITHUB_OUTPUT; else echo "BUILD_EXISTS=no" >> $GITHUB_OUTPUT; fi
id: build-exists
- name: Build in docker
if: ${{ matrix.docker && steps.build-exists.outputs.BUILD_EXISTS == 'no' }}
run: |
docker run --rm \
-e CI -e RUST_BACKTRACE -e CARGO_TERM_COLOR \
-e CARGO_INCREMENTAL=0 -e CARGO_REGISTRIES_CRATES_IO_PROTOCOL -e TURBO_API \
-e TURBO_TEAM -e TURBO_TOKEN -e TURBO_VERSION -e TURBO_CACHE="remote:rw" \
-e TARGET="${{ matrix.target }}" \
-e ABI="${{ matrix.abi }}" \
-e ARCH="${{ matrix.arch }}" \
-e BUILD_TASK="${{ matrix.build_task }}" \
-v ${{ env.HOME }}/.cargo/git:/root/.cargo/git \
-v ${{ env.HOME }}/.cargo/registry:/root/.cargo/registry \
-v ${{ github.workspace }}:/build \
-w /build \
--entrypoint bash \
${{ matrix.docker }} \
-xeo pipefail scripts/docker-native-build.sh
- name: cache build
if: ${{ matrix.docker && steps.build-exists.outputs.BUILD_EXISTS == 'no' }}
run: pnpm dlx turbo@${TURBO_VERSION} run cache-build-native --force -- ${{ matrix.target }}
- name: 'Build'
if: ${{ !matrix.docker }}
env:
# We don't want incremental builds on CI
CARGO_INCREMENTAL: 0
run: |
echo "Host arch: $(uname -m)"
echo "Node arch: $(node -e 'console.log(process.arch)')"
echo "Node binary: $(file $(which node))"
echo "Rustc binary: $(file $(which rustc) || echo 'not found')"
rustc --version --verbose || true
node -v
npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}"
pnpm dlx turbo@${TURBO_VERSION} run ${{ matrix.build_task }} ${TURBO_ARGS} -- --target ${{ matrix.target }}
if [ "${{ matrix.os }}" != "windows" ]; then
strip -x packages/next-swc/native/next-swc.*.node
fi
- name: 'check build cache status'
id: check-did-build
run: if [[ ! -z $(ls packages/next-swc/native) ]]; then echo "DID_BUILD=true" >> $GITHUB_OUTPUT; fi
- name: 'Report binary size'
if: ${{ steps.check-did-build.outputs.DID_BUILD == 'true' }}
run: |
shopt -s nullglob
for f in packages/next-swc/native/next-swc.*.node; do
FILE="$f" node -e "const s=require('fs').statSync(process.env.FILE).size; console.log('::notice title=${{ matrix.target }} binary size::' + (s/1024/1024).toFixed(1) + ' MB (' + s + ' bytes)')"
done
# Try to upload metrics for Turbopack to datadog's CI pipeline execution
- name: 'Collect turbopack build metrics'
id: check-turbopack-bytesize
if: ${{ steps.check-did-build.outputs.DID_BUILD == 'true' }}
continue-on-error: true
run: |
mkdir -p ./turbopack-bin-size
shopt -s nullglob
for filename in packages/next-swc/native/next-swc.*.node; do
# Strip out filename to extract target triple
export FILENAME=$(basename ${filename})
export FILENAME=${FILENAME#*.}
export FILENAME=${FILENAME%.node}
export BYTESIZE=$(wc -c < $filename | xargs)
echo "Reporting $FILENAME:$BYTESIZE for Turbopack bytesize"
echo "turbopack.bytesize.$FILENAME:$BYTESIZE" > ./turbopack-bin-size/${{ matrix.target }}
done
- name: Upload turbopack bytesize artifact
if: ${{ steps.check-did-build.outputs.DID_BUILD == 'true' }}
uses: actions/upload-artifact@v6
with:
name: turbopack-bytesize-${{ matrix.target }}
path: turbopack-bin-size/*
- name: Upload swc artifact
uses: actions/upload-artifact@v6
with:
name: next-swc-binaries-${{ matrix.target }}
path: packages/next-swc/native/next-swc.*.node
- name: Upload turbo summary artifact
if: ${{ !matrix.docker }}
uses: actions/upload-artifact@v6
with:
name: turbo-run-summary-${{ matrix.target }}
path: .turbo/runs
build-wasm:
if: ${{ needs.deploy-target.outputs.value != 'skipped' }}
needs:
- deploy-target
strategy:
matrix:
target: [web, nodejs]
runs-on:
- 'self-hosted'
- 'linux'
- 'x64'
- 'metal'
steps:
- uses: actions/checkout@v6
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- run: corepack enable
- name: Install Rust
uses: ./.github/actions/setup-rust
with:
targets: wasm32-unknown-unknown
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: normalize versions
run: node scripts/normalize-version-bump.js
- name: Build
run: pnpm dlx turbo@${TURBO_VERSION} run build-wasm ${TURBO_ARGS} -- --target ${{ matrix.target }}
- name: Add target to folder name
run: '[[ -d "crates/wasm/pkg" ]] && mv crates/wasm/pkg crates/wasm/pkg-${{ matrix.target }} || ls crates/wasm'
- name: Upload turbo summary artifact
uses: actions/upload-artifact@v6
with:
name: turbo-run-summary-wasm-${{matrix.target}}
path: .turbo/runs
- name: Upload swc artifact
uses: actions/upload-artifact@v6
with:
name: wasm-binaries-${{matrix.target}}
path: crates/wasm/pkg-*
deploy-tarball:
if: ${{ needs.deploy-target.outputs.value != 'production' }}
name: Deploy preview tarball
runs-on: ubuntu-latest
needs:
- deploy-target
- build
- build-wasm
- build-native
steps:
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/cache@v5
timeout-minutes: 5
id: restore-build
with:
path: ./*
# Cache includes repo checkout which is required for later scripts
fail-on-cache-miss: true
key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }}
restore-keys: |
${{ github.sha }}-${{ github.run_number }}
${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt}}
- uses: actions/download-artifact@v8
with:
pattern: next-swc-binaries-*
merge-multiple: true
path: packages/next-swc/native
- uses: actions/download-artifact@v8
with:
pattern: wasm-binaries-*
merge-multiple: true
path: crates/wasm
- name: Create tarballs
# github.event.after is available on push and pull_request#synchronize events.
# For workflow_dispatch events, github.sha is the head commit.
run: node scripts/create-preview-tarballs.js "${{ github.event.after || github.sha }}" "${{ runner.temp }}/preview-tarballs"
- name: Upload tarballs
uses: actions/upload-artifact@v6
with:
# Update all mentions of this name in vercel-packages when changing.
name: preview-tarballs
path: ${{ runner.temp }}/preview-tarballs/*
publishRelease:
if: ${{ needs.deploy-target.outputs.value == 'production' }}
name: Potentially publish release
runs-on: ubuntu-latest
needs:
- deploy-target
- build
- build-wasm
- build-native
permissions:
contents: write
id-token: write
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }}
steps:
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/cache@v5
timeout-minutes: 5
id: restore-build
with:
path: ./*
# Cache includes repo checkout which is required for later scripts
fail-on-cache-miss: true
key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }}
restore-keys: |
${{ github.sha }}-${{ github.run_number }}
${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt}}
- uses: actions/download-artifact@v8
with:
pattern: next-swc-binaries-*
merge-multiple: true
path: packages/next-swc/native
- uses: actions/download-artifact@v8
with:
pattern: wasm-binaries-*
merge-multiple: true
path: crates/wasm
- run: npm i -g npm@10.4.0 # need latest version for provenance (pinning to avoid bugs)
- run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
- run: ./scripts/publish-native.js
- run: ./scripts/publish-release.js
env:
RELEASE_BOT_GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
publish-turbopack-npm-packages:
# Matches the commit message written by turbopack/xtask/src/publish.rs:377
if: "${{(github.ref == 'refs/heads/canary') && startsWith(github.event.head_commit.message, 'chore: release turbopack npm packages')}}"
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- uses: ./.github/actions/setup-rust
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpx turbo@canary run build --only --filter='./turbopack/packages/*'
- name: Write NPM_TOKEN
run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN_ELEVATED }}" > ~/.npmrc
- name: Publish
run: cargo xtask workspace --publish
deployExamples:
if: ${{ needs.deploy-target.outputs.value != 'automated-preview' }}
name: Deploy examples
runs-on: ubuntu-latest
needs: [build, deploy-target]
steps:
- run: echo '${{ needs.deploy-target.outputs.value }}'
- uses: actions/checkout@v6
with:
fetch-depth: 25
- name: Install Vercel CLI
run: npm i -g vercel@latest
- name: Deploy preview examples
if: ${{ needs.deploy-target.outputs.value != 'production' }}
run: ./scripts/deploy-examples.sh
env:
VERCEL_API_TOKEN: ${{ secrets.VERCEL_API_TOKEN }}
DEPLOY_ENVIRONMENT: preview
- name: Deploy production examples
if: ${{ needs.deploy-target.outputs.value == 'production' }}
run: ./scripts/deploy-examples.sh
env:
VERCEL_API_TOKEN: ${{ secrets.VERCEL_API_TOKEN }}
DEPLOY_ENVIRONMENT: production
buildPassed:
needs: ['deploy-target', 'build', 'build-wasm', 'build-native']
if: ${{ always() && needs.deploy-target.outputs.value != '' }}
# Coupled with retry logic in retry_test.yml
name: thank you, build
runs-on: ubuntu-latest
steps:
- run: exit 1
if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
upload_turbopack_bytesize:
if: ${{ needs.deploy-target.outputs.value != 'automated-preview'}}
name: Upload Turbopack Bytesize metrics to Datadog
runs-on: ubuntu-latest
needs: [build-native, deploy-target]
env:
DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }}
steps:
- name: Collect bytesize metrics
uses: actions/download-artifact@v8
with:
pattern: turbopack-bytesize-*
merge-multiple: true
path: turbopack-bin-size
- name: Upload to Datadog
run: |
ls -al turbopack-bin-size
for filename in turbopack-bin-size/*; do
export BYTESIZE+=" --metrics $(cat $filename)"
done
echo "Reporting $BYTESIZE"
npx @datadog/datadog-ci@2.23.1 metric --no-fail --level pipeline $BYTESIZE
build_and_test matrix .github/workflows/build_and_test.yml
View raw YAML
name: build-and-test
on:
push:
branches: ['canary']
pull_request:
types: [opened, synchronize]
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) }}
cancel-in-progress: true
# NOTE: anything in `afterBuild` inherits environment variables defined in
# `build_reusable.yml` (not these!) because that job executes within the context
# of that workflow. Environment variables are not automatically passed to
# reusable workflows.
env:
NODE_MAINTENANCE_VERSION: 20
NODE_LTS_VERSION: 22
jobs:
optimize-ci:
uses: ./.github/workflows/graphite_ci_optimizer.yml
secrets: inherit
changes:
name: Determine changes
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
- name: check for docs only change
id: docs-change
run: |
echo "DOCS_ONLY<<EOF" >> $GITHUB_OUTPUT;
echo "$(node scripts/run-for-change.mjs --not --type docs --exec echo 'false')" >> $GITHUB_OUTPUT;
echo 'EOF' >> $GITHUB_OUTPUT
- name: check for release
id: is-release
run: |
if [[ $(node ./scripts/check-is-release.js 2> /dev/null || :) == v* ]];
then
echo "IS_RELEASE=true" >> $GITHUB_OUTPUT
else
echo "IS_RELEASE=false" >> $GITHUB_OUTPUT
fi
outputs:
docs-only: ${{ steps.docs-change.outputs.DOCS_ONLY != 'false' }}
is-release: ${{ steps.is-release.outputs.IS_RELEASE == 'true' }}
rspack: >-
${{
steps.is-release.outputs.IS_RELEASE == 'true' || (
github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'Rspack')
)
}}
build-native:
name: build-native
uses: ./.github/workflows/build_reusable.yml
needs: ['changes']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
with:
skipInstallBuild: 'yes'
stepName: 'build-native'
secrets: inherit
build-native-windows:
name: build-native-windows
uses: ./.github/workflows/build_reusable.yml
needs: ['changes']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
with:
skipInstallBuild: 'yes'
stepName: 'build-native-windows'
runs_on_labels: '["windows","self-hosted","x64"]'
buildNativeTarget: 'x86_64-pc-windows-msvc'
secrets: inherit
build-next:
name: build-next
uses: ./.github/workflows/build_reusable.yml
with:
skipNativeBuild: 'yes'
stepName: 'build-next'
secrets: inherit
fetch-test-timings:
name: fetch test timings
runs-on: ubuntu-latest
needs: ['changes']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup pnpm
run: |
npm i -g corepack@0.31
corepack enable
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 25
- name: Install dependencies
run: pnpm install
- name: Fetch test timings
run: node run-tests.js --timings --write-timings -g 1/1
continue-on-error: true
env:
KV_REST_API_URL: ${{ secrets.KV_REST_API_URL }}
KV_REST_API_TOKEN: ${{ secrets.KV_REST_API_TOKEN }}
- name: Ensure test timings file exists
run: |
if [ ! -f test-timings.json ]; then
echo "No timings fetched, creating empty timings file"
echo '{}' > test-timings.json
fi
- name: Upload test timings
uses: actions/upload-artifact@v4
with:
name: test-timings
path: test-timings.json
# Allows "rerun failed jobs" for N days
retention-days: 5
if-no-files-found: error
lint:
name: lint
needs: ['build-next']
uses: ./.github/workflows/build_reusable.yml
with:
skipNativeBuild: 'yes'
skipNativeInstall: 'yes'
afterBuild: |
pnpm lint-no-typescript
pnpm check-examples
pnpm validate-externals-doc
stepName: 'lint'
secrets: inherit
validate-docs-links:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: 'Run link checker'
run: node ./.github/actions/validate-docs-links/dist/index.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
check-types-precompiled:
name: types and precompiled
needs: ['changes', 'build-native', 'build-next']
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: pnpm types-and-precompiled
stepName: 'types-and-precompiled'
secrets: inherit
test-cargo-unit:
name: test cargo unit
needs: ['changes', 'build-next']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
needsRust: 'yes'
needsNextest: 'yes'
skipNativeBuild: 'yes'
afterBuild: pnpm dlx turbo@${TURBO_VERSION} run test-cargo-unit --remote-cache-timeout 60 --log-order stream
stepName: 'test-cargo-unit'
secrets: inherit
test-bench:
name: test cargo benches
needs: ['optimize-ci', 'changes', 'build-next']
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/test-turbopack-rust-bench-test.yml
secrets: inherit
rust-check:
name: rust check
needs: ['changes', 'build-next']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
needsRust: 'yes'
skipInstallBuild: 'yes'
skipNativeBuild: 'yes'
afterBuild: pnpm dlx turbo@${TURBO_VERSION} run rust-check
stepName: 'rust-check'
secrets: inherit
rustdoc-check:
name: rustdoc check
needs: ['changes', 'build-next']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
needsRust: 'yes'
skipInstallBuild: 'yes'
skipNativeBuild: 'yes'
afterBuild: ./scripts/deploy-turbopack-docs.sh
stepName: 'rustdoc-check'
secrets: inherit
ast-grep:
needs: ['changes', 'build-next']
runs-on: ubuntu-latest
name: ast-grep lint
steps:
- uses: actions/checkout@v4
- name: ast-grep lint step
uses: ast-grep/action@v1.5.0
with:
# Keep in sync with the next.js repo's root package.json
version: 0.31.0
devlow-bench:
name: Run devlow benchmarks
needs: ['optimize-ci', 'changes', 'build-next', 'build-native']
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' && github.event_name != 'pull_request' }}
strategy:
fail-fast: false
matrix:
mode:
- '--turbopack=false'
- '--turbopack=true'
selector:
- '--scenario=heavy-npm-deps-dev --page=homepage'
- '--scenario=heavy-npm-deps-build --page=homepage'
- '--scenario=heavy-npm-deps-build-turbo-cache-enabled --page=homepage'
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
./node_modules/.bin/devlow-bench ./scripts/devlow-bench.mjs \
--datadog=ubuntu-latest-16-core \
${{ matrix.mode }} \
${{ matrix.selector }}
stepName: 'devlow-bench-${{ matrix.mode }}-${{ matrix.selector }}'
secrets: inherit
test-devlow:
name: test devlow package
needs: ['optimize-ci', 'changes']
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
skipNativeBuild: 'yes'
stepName: 'test-devlow'
afterBuild: |
pnpm run --filter=devlow-bench test
secrets: inherit
test-turbopack-dev:
name: test turbopack dev
needs:
[
'optimize-ci',
'changes',
'build-next',
'build-native',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
exclude:
# Excluding React 18 tests unless on `canary` branch until budget is approved.
- react: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'run-react-18-tests') && '18.3.1' }}
group: [1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 7/7]
# Empty value uses default
react: ['', '18.3.1']
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export IS_TURBOPACK_TEST=1
export TURBOPACK_DEV=1
export NEXT_TEST_MODE=dev
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
export RUST_BACKTRACE=1
node run-tests.js \
--test-pattern '^(test\/(development|e2e))/.*\.test\.(js|jsx|ts|tsx)$' \
--timings \
--require-timings \
-g ${{ matrix.group }}
testTimingsArtifact: 'test-timings'
stepName: 'test-turbopack-dev-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-turbopack-integration:
name: test turbopack integration
needs:
[
'optimize-ci',
'changes',
'build-native',
'build-next',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
group:
- 1/13
- 2/13
- 3/13
- 4/13
- 5/13
- 6/13
- 7/13
- 8/13
- 9/13
- 10/13
- 11/13
- 12/13
- 13/13
# Empty value uses default
# TODO: Run with React 18.
# Integration tests use the installed React version in next/package.json.
# We can't easily switch like we do for e2e tests.
# Skipping this dimension until we can figure out a way to test multiple React versions.
react: ['']
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
export IS_TURBOPACK_TEST=1
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
export RUST_BACKTRACE=1
node run-tests.js \
--timings \
--require-timings \
-g ${{ matrix.group }} \
--type integration
testTimingsArtifact: 'test-timings'
stepName: 'test-turbopack-integration-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-turbopack-production:
name: test turbopack production
needs:
[
'optimize-ci',
'changes',
'build-next',
'build-native',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
exclude:
# Excluding React 18 tests unless on `canary` branch until budget is approved.
- react: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'run-react-18-tests') && '18.3.1' }}
group: [1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 7/7]
# Empty value uses default
react: ['', '18.3.1']
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
export IS_TURBOPACK_TEST=1
export TURBOPACK_BUILD=1
export NEXT_TEST_MODE=start
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
export RUST_BACKTRACE=1
node run-tests.js --timings --require-timings -g ${{ matrix.group }} --type production
testTimingsArtifact: 'test-timings'
stepName: 'test-turbopack-production-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-rspack-dev:
name: test rspack dev
needs:
[
'optimize-ci',
'changes',
'build-next',
'build-native',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' && needs.changes.outputs.rspack == 'true' }}
strategy:
fail-fast: false
matrix:
exclude:
# Excluding React 18 tests unless on `canary` branch until budget is approved.
- react: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'run-react-18-tests') && '18.3.1' }}
group: [1/5, 2/5, 3/5, 4/5, 5/5]
# Empty value uses default
react: ['', '18.3.1']
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/test/rspack-dev-tests-manifest.json"
export NEXT_TEST_MODE=dev
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
# rspack flags
export NEXT_RSPACK=1
export NEXT_TEST_USE_RSPACK=1
# HACK: Despite the name, this environment variable is only used to gate
# tests, so it's applicable to rspack
export TURBOPACK_DEV=1
node run-tests.js \
--test-pattern '^(test\/(development|e2e))/.*\.test\.(js|jsx|ts|tsx)$' \
--timings \
--require-timings \
-g ${{ matrix.group }}
testTimingsArtifact: 'test-timings'
stepName: 'test-rspack-dev-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-rspack-integration:
name: test rspack development integration
needs:
[
'optimize-ci',
'changes',
'build-next',
'build-native',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' && needs.changes.outputs.rspack == 'true' }}
strategy:
fail-fast: false
matrix:
group: [1/6, 2/6, 3/6, 4/6, 5/6, 6/6]
# Empty value uses default
# TODO: Run with React 18.
# Integration tests use the installed React version in next/package.json.
# We can't easily switch like we do for e2e tests.
# Skipping this dimension until we can figure out a way to test multiple React versions.
react: ['']
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
export NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/test/rspack-dev-tests-manifest.json"
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
# rspack flags
export NEXT_RSPACK=1
export NEXT_TEST_USE_RSPACK=1
# HACK: Despite the name, this environment variable is only used to gate
# tests, so it's applicable to rspack
export TURBOPACK_DEV=1
node run-tests.js \
--timings \
--require-timings \
-g ${{ matrix.group }} \
--type integration
testTimingsArtifact: 'test-timings'
stepName: 'test-rspack-integration-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-rspack-production:
name: test rspack production
needs:
[
'optimize-ci',
'changes',
'build-next',
'build-native',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' && needs.changes.outputs.rspack == 'true' }}
strategy:
fail-fast: false
matrix:
exclude:
# Excluding React 18 tests unless on `canary` branch until budget is approved.
- react: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'run-react-18-tests') && '18.3.1' }}
group: [1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 7/7]
# Empty value uses default
react: ['', '18.3.1']
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
export NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/test/rspack-build-tests-manifest.json"
export NEXT_TEST_MODE=start
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
# rspack flags
export NEXT_RSPACK=1
export NEXT_TEST_USE_RSPACK=1
# HACK: Despite the name, this environment variable is only used to gate
# tests, so it's applicable to rspack
export TURBOPACK_BUILD=1
node run-tests.js --timings --require-timings -g ${{ matrix.group }} --type production
testTimingsArtifact: 'test-timings'
stepName: 'test-rspack-production-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-rspack-production-integration:
name: test rspack production integration
needs:
[
'optimize-ci',
'changes',
'build-next',
'build-native',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' && needs.changes.outputs.rspack == 'true' }}
strategy:
fail-fast: false
matrix:
group: [1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 7/7]
# Empty value uses default
# TODO: Run with React 18.
# Integration tests use the installed React version in next/package.json.
# We can't easily switch like we do for e2e tests.
# Skipping this dimension until we can figure out a way to test multiple React versions.
react: ['']
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
export NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/test/rspack-build-tests-manifest.json"
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
# rspack flags
export NEXT_RSPACK=1
export NEXT_TEST_USE_RSPACK=1
# HACK: Despite the name, this environment variable is only used to gate
# tests, so it's applicable to rspack
export TURBOPACK_BUILD=1
node run-tests.js \
--timings \
--require-timings \
-g ${{ matrix.group }} \
--type integration
testTimingsArtifact: 'test-timings'
stepName: 'test-rspack-production-integration-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-next-swc-wasm:
name: test next-swc wasm
needs: ['optimize-ci', 'changes', 'build-next']
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
skipNativeBuild: 'yes'
skipNativeInstall: 'yes'
afterBuild: |
rustup target add wasm32-unknown-unknown
node ./scripts/normalize-version-bump.js
pnpm dlx turbo@${TURBO_VERSION} run build-wasm -- --target nodejs
git checkout .
export NEXT_TEST_MODE=start
export NEXT_TEST_WASM=true
export IS_WEBPACK_TEST=1
node run-tests.js \
test/production/pages-dir/production/test/index.test.ts \
test/e2e/streaming-ssr/index.test.ts
stepName: 'test-next-swc-wasm'
secrets: inherit
#[NOTE] currently this only checks building wasi target
test-next-napi-bindings-wasi:
name: test next-swc wasi
needs: ['optimize-ci', 'changes', 'build-next']
# TODO: Re-enable this when https://github.com/napi-rs/napi-rs/issues/2009 is addressed.
# Specifically, the `platform` value is now `threads` in
# https://github.com/napi-rs/napi-rs/blob/e4ad4767efaf093fdff3dc768856f6100a6e3f72/cli/src/api/build.ts#L530
if: false
# if: ${{ needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
skipNativeBuild: 'yes'
skipNativeInstall: 'yes'
afterBuild: |
rustup target add wasm32-wasip1-threads
pnpm dlx turbo@${TURBO_VERSION} run build-native-wasi
stepName: 'test-next-napi-bindings-wasi'
secrets: inherit
test-unit:
name: test unit
needs: ['changes', 'build-next', 'build-native']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
node: [20, 22] # TODO: use env var like [env.NODE_MAINTENANCE_VERSION, env.NODE_LTS_VERSION]
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: ${{ matrix.node }}
afterBuild: node run-tests.js --type unit
stepName: 'test-unit-${{ matrix.node }}'
secrets: inherit
# TODO: Remove this once we bump minimum Node.js version to v22
test-next-config-ts-native-ts-dev:
name: test next-config-ts-native-ts dev
needs: ['changes', 'build-next', 'build-native']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
node: [22, 24]
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: ${{ matrix.node }}
afterBuild: |
export __NEXT_NODE_NATIVE_TS_LOADER_ENABLED=true
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
NEXT_TEST_MODE=dev NODE_OPTIONS=--experimental-transform-types node run-tests.js test/e2e/app-dir/next-config-ts-native-ts/**/*.test.ts test/e2e/app-dir/next-config-ts-native-mts/**/*.test.ts
stepName: 'test-next-config-ts-native-ts-dev-${{ matrix.node }}'
secrets: inherit
# TODO: Remove this once we bump minimum Node.js version to v22
test-next-config-ts-native-ts-prod:
name: test next-config-ts-native-ts prod
needs: ['changes', 'build-next', 'build-native']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
node: [22, 24]
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: ${{ matrix.node }}
afterBuild: |
export __NEXT_NODE_NATIVE_TS_LOADER_ENABLED=true
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
NEXT_TEST_MODE=start NODE_OPTIONS=--experimental-transform-types node run-tests.js test/e2e/app-dir/next-config-ts-native-ts/**/*.test.ts test/e2e/app-dir/next-config-ts-native-mts/**/*.test.ts
stepName: 'test-next-config-ts-native-ts-prod-${{ matrix.node }}'
secrets: inherit
test-unit-windows:
name: test unit windows
needs: ['changes', 'build-native', 'build-native-windows']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
node: [20, 22] # TODO: use env var like [env.NODE_MAINTENANCE_VERSION, env.NODE_LTS_VERSION]
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: ${{ matrix.node }}
afterBuild: node run-tests.js --type unit
stepName: 'test-unit-windows-${{ matrix.node }}'
runs_on_labels: '["windows","self-hosted","x64"]'
buildNativeTarget: 'x86_64-pc-windows-msvc'
secrets: inherit
test-new-tests-dev:
name: Test new and changed tests for flakes (dev)
needs: ['optimize-ci', 'changes', 'build-native', 'build-next']
# test-new-tests-if
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
# test-new-tests-end-if
strategy:
fail-fast: false
matrix:
group: [1/5, 2/5, 3/5, 4/5, 5/5]
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node scripts/test-new-tests.mjs \
--flake-detection \
--mode dev \
--group ${{ matrix.group }}
stepName: 'test-new-tests-dev-${{matrix.group}}'
timeout_minutes: 60 # Increase the default timeout as tests are intentionally run multiple times to detect flakes
secrets: inherit
test-new-tests-start:
name: Test new and changed tests for flakes (prod)
needs: ['optimize-ci', 'changes', 'build-native', 'build-next']
# test-new-tests-if
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
# test-new-tests-end-if
strategy:
fail-fast: false
matrix:
group: [1/5, 2/5, 3/5, 4/5, 5/5]
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node scripts/test-new-tests.mjs \
--flake-detection \
--mode start \
--group ${{ matrix.group }}
stepName: 'test-new-tests-start-${{matrix.group}}'
timeout_minutes: 60 # Increase the default timeout as tests are intentionally run multiple times to detect flakes
secrets: inherit
test-new-tests-deploy:
name: Test new and changed tests when deployed
needs:
['optimize-ci', 'test-prod', 'test-new-tests-dev', 'test-new-tests-start']
# test-new-tests-if
if: ${{ needs.optimize-ci.outputs.skip == 'false' }}
# test-new-tests-end-if
strategy:
fail-fast: false
matrix:
group: [1/5, 2/5, 3/5, 4/5, 5/5]
uses: ./.github/workflows/build_reusable.yml
with:
# Keep Next.js related env variables in sync with additionalEnv in next-deploy.ts
afterBuild: |
export NEXT_ENABLE_ADAPTER=1
export NEXT_E2E_TEST_TIMEOUT=240000
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node scripts/test-new-tests.mjs \
--mode deploy \
--group ${{ matrix.group }}
stepName: 'test-new-tests-deploy-${{matrix.group}}'
secrets: inherit
test-new-tests-deploy-cache-components:
name: Test new and changed tests when deployed (cache components)
needs:
[
'optimize-ci',
'test-cache-components-prod',
'test-new-tests-dev',
'test-new-tests-start',
]
# test-new-tests-if
if: ${{ needs.optimize-ci.outputs.skip == 'false' }}
# test-new-tests-end-if
strategy:
fail-fast: false
matrix:
group: [1/5, 2/5, 3/5, 4/5, 5/5]
uses: ./.github/workflows/build_reusable.yml
with:
# Keep Next.js related env variables in sync with additionalEnv in next-deploy.ts
afterBuild: |
export __NEXT_CACHE_COMPONENTS=true
export __NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS=true
export __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=true
export NEXT_ENABLE_ADAPTER=1
export NEXT_EXTERNAL_TESTS_FILTERS="test/deploy-tests-manifest.json,test/cache-components-tests-manifest.json"
export NEXT_E2E_TEST_TIMEOUT=240000
node scripts/test-new-tests.mjs \
--mode deploy \
--group ${{ matrix.group }}
stepName: 'test-new-tests-deploy-cache-components-${{matrix.group}}'
secrets: inherit
test-dev: # TODO: rename to include webpack
name: test dev
needs:
[
'optimize-ci',
'changes',
'build-native',
'build-next',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
exclude:
# Excluding React 18 tests unless on `canary` branch until budget is approved.
- react: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'run-react-18-tests') && '18.3.1' }}
group: [1/10, 2/10, 3/10, 4/10, 5/10, 6/10, 7/10, 8/10, 9/10, 10/10]
# Empty value uses default
react: ['', '18.3.1']
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export IS_WEBPACK_TEST=1
export NEXT_TEST_MODE=dev
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node run-tests.js \
--timings \
--require-timings \
-g ${{ matrix.group }} \
--type development
testTimingsArtifact: 'test-timings'
stepName: 'test-dev-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-dev-windows:
name: test dev windows
needs:
[
'optimize-ci',
'changes',
'build-native-windows',
'build-native',
'build-next',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
# Should this be using turbopack? a variation?
afterBuild: |
export NEXT_TEST_MODE=dev
export IS_WEBPACK_TEST=1
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node run-tests.js \
test/e2e/app-dir/app/index.test.ts \
test/e2e/app-dir/app-edge/app-edge.test.ts \
test/e2e/app-dir/proxy-runtime-nodejs/proxy-runtime-nodejs.test.ts \
test/development/app-dir/segment-explorer/segment-explorer.test.ts
stepName: 'test-dev-windows'
runs_on_labels: '["windows","self-hosted","x64"]'
buildNativeTarget: 'x86_64-pc-windows-msvc'
secrets: inherit
test-integration-windows:
name: test integration windows
needs:
[
'optimize-ci',
'changes',
'build-native-windows',
'build-native',
'build-next',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
export IS_WEBPACK_TEST=1
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node run-tests.js \
--concurrency 4 \
test/production/pages-dir/production/test/index.test.ts \
test/integration/css-client-nav/test/index.test.ts \
test/integration/rewrites-has-condition/test/index.test.ts \
test/integration/create-next-app/index.test.ts \
test/integration/create-next-app/package-manager/pnpm.test.ts
stepName: 'test-integration-windows'
runs_on_labels: '["windows","self-hosted","x64"]'
buildNativeTarget: 'x86_64-pc-windows-msvc'
secrets: inherit
test-prod-windows:
name: test prod windows
needs:
[
'optimize-ci',
'changes',
'build-native-windows',
'build-native',
'build-next',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export NEXT_TEST_MODE=start
export IS_WEBPACK_TEST=1
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node run-tests.js --type production \
test/e2e/app-dir/app/index.test.ts \
test/e2e/app-dir/app-edge/app-edge.test.ts \
test/e2e/app-dir/metadata-edge/index.test.ts \
test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts \
test/e2e/app-dir/proxy-runtime-nodejs/proxy-runtime-nodejs.test.ts
stepName: 'test-prod-windows'
runs_on_labels: '["windows","self-hosted","x64"]'
buildNativeTarget: 'x86_64-pc-windows-msvc'
secrets: inherit
test-prod:
name: test prod
needs:
[
'optimize-ci',
'changes',
'build-native',
'build-next',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
exclude:
# Excluding React 18 tests unless on `canary` branch until budget is approved.
- react: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'run-react-18-tests') && '18.3.1' }}
group: [1/10, 2/10, 3/10, 4/10, 5/10, 6/10, 7/10, 8/10, 9/10, 10/10]
# Empty value uses default
react: ['', '18.3.1']
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export IS_WEBPACK_TEST=1
export NEXT_TEST_MODE=start
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node run-tests.js --timings --require-timings -g ${{ matrix.group }} --type production
testTimingsArtifact: 'test-timings'
stepName: 'test-prod-react-${{ matrix.react }}-${{ matrix.group }}'
secrets: inherit
test-integration:
name: test integration
needs:
[
'optimize-ci',
'changes',
'build-native',
'build-next',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
group:
- 1/13
- 2/13
- 3/13
- 4/13
- 5/13
- 6/13
- 7/13
- 8/13
- 9/13
- 10/13
- 11/13
- 12/13
- 13/13
# Empty value uses default
# TODO: Run with React 18.
# Integration tests use the installed React version in next/package.json.
# We can't easily switch like we do for e2e tests.
# Skipping this dimension until we can figure out a way to test multiple React versions.
react: ['']
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
export IS_WEBPACK_TEST=1
export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
node run-tests.js \
--timings \
--require-timings \
-g ${{ matrix.group }} \
--type integration
testTimingsArtifact: 'test-timings'
stepName: 'test-integration-${{ matrix.group }}-react-${{ matrix.react }}'
secrets: inherit
test-firefox-safari:
name: test firefox and safari
needs: ['optimize-ci', 'changes', 'build-native', 'build-next']
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
browser: 'firefox webkit'
afterBuild: |
pnpm playwright install
# these all run without concurrency because they're heavier
export TEST_CONCURRENCY=1
export IS_WEBPACK_TEST=1
BROWSER_NAME=firefox node run-tests.js \
test/production/pages-dir/production/test/index.test.ts \
test/production/chunk-load-failure/chunk-load-failure.test.ts
NEXT_TEST_MODE=start BROWSER_NAME=safari node run-tests.js \
test/production/pages-dir/production/test/index.test.ts \
test/production/chunk-load-failure/chunk-load-failure.test.ts \
test/e2e/basepath/basepath.test.ts \
test/e2e/basepath/error-pages.test.ts
BROWSER_NAME=safari DEVICE_NAME='iPhone XR' node run-tests.js \
test/production/prerender-prefetch/index.test.ts
stepName: 'test-firefox-safari'
secrets: inherit
# Manifest generated via: https://gist.github.com/wyattjoh/2ceaebd82a5bcff4819600fd60126431
test-cache-components-integration:
name: test cache components integration
needs: ['optimize-ci', 'changes', 'build-native', 'build-next']
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
export __NEXT_CACHE_COMPONENTS=true
export __NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS=true
export __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=true
export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json"
export IS_WEBPACK_TEST=1
node run-tests.js \
--timings \
--type integration
stepName: 'test-cache-components-integration'
secrets: inherit
test-cache-components-dev:
name: test cache components dev
needs:
[
'optimize-ci',
'changes',
'build-native',
'build-next',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
group: [1/6, 2/6, 3/6, 4/6, 5/6, 6/6]
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export __NEXT_CACHE_COMPONENTS=true
export __NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS=true
export __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=true
export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json"
export NEXT_TEST_MODE=dev
export IS_WEBPACK_TEST=1
node run-tests.js \
--timings \
--require-timings \
-g ${{ matrix.group }} \
--type development
testTimingsArtifact: 'test-timings'
stepName: 'test-cache-components-dev-${{ matrix.group }}'
secrets: inherit
test-cache-components-prod:
name: test cache components prod
needs:
[
'optimize-ci',
'changes',
'build-native',
'build-next',
'fetch-test-timings',
]
if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }}
strategy:
fail-fast: false
matrix:
group: [1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 7/7]
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export __NEXT_CACHE_COMPONENTS=true
export __NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS=true
export __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=true
export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json"
export NEXT_TEST_MODE=start
export IS_WEBPACK_TEST=1
node run-tests.js \
--timings \
--require-timings \
-g ${{ matrix.group }} \
--type production
testTimingsArtifact: 'test-timings'
stepName: 'test-cache-components-prod-${{ matrix.group }}'
secrets: inherit
tests-pass:
needs:
[
'build-native',
'build-next',
'lint',
'validate-docs-links',
'check-types-precompiled',
'test-unit',
'test-next-config-ts-native-ts-dev',
'test-next-config-ts-native-ts-prod',
'test-dev',
'test-prod',
'test-integration',
'test-firefox-safari',
'test-cache-components-dev',
'test-cache-components-prod',
'test-cache-components-integration',
'test-cargo-unit',
'rust-check',
'rustdoc-check',
'test-next-swc-wasm',
'test-turbopack-dev',
'test-turbopack-integration',
'test-new-tests-dev',
'test-new-tests-start',
'test-new-tests-deploy',
'test-new-tests-deploy-cache-components',
'test-turbopack-production',
'test-unit-windows',
'test-dev-windows',
'test-integration-windows',
'test-prod-windows',
]
if: always()
runs-on: ubuntu-latest
# Coupled with retry logic in retry_test.yml
name: thank you, next
steps:
- run: exit 1
if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
build_reusable .github/workflows/build_reusable.yml
View raw YAML
name: Build Reusable
on:
workflow_call:
inputs:
afterBuild:
required: false
description: 'additional steps to run'
type: string
skipInstallBuild:
required: false
description: 'whether to skip pnpm install && pnpm build'
type: string
skipNativeBuild:
required: false
description: 'whether to skip building native modules'
type: string
skipNativeInstall:
required: false
description: 'whether to skip native postinstall script'
type: string
default: 'yes'
uploadAnalyzerArtifacts:
required: false
description: 'whether to upload analyzer artifacts'
type: string
nodeVersion:
required: false
description: 'version of Node.js to use'
type: string
needsRust:
required: false
description: 'if rust is needed'
type: string
needsNextest:
required: false
description: 'if nextest rust dep is needed'
type: string
rustBuildProfile:
required: false
description: 'The profile to use for the build, default is `release-with-assertions`, also supports `` for debug and `release` for normal release'
type: string
default: 'release-with-assertions'
uploadSwcArtifact:
required: false
description: 'if swc artifact needs uploading'
type: string
rustCacheKey:
required: false
description: 'rustCacheKey to cache shared target assets'
type: string
stepName:
required: true
description: 'name of the step, to be used for the upload artifact unique key '
type: string
timeout_minutes:
description: 'Timeout in minutes'
required: false
type: number
default: 30
runs_on_labels:
description: 'List of runner labels'
required: false
type: string
default: '["self-hosted", "linux", "x64", "metal"]'
buildNativeTarget:
description: 'Target for build-native step'
required: false
type: string
default: 'x86_64-unknown-linux-gnu'
overrideProxyAddress:
description: Override the proxy address to use for the test
required: false
type: string
default: ''
testTimingsArtifact:
description: 'Name of an uploaded artifact containing test-timings.json. When set, download it instead of fetching via turbo.'
required: false
type: string
default: ''
testReportsArtifactPrefix:
description: 'Artifact name prefix for uploading test `*.results.json` files. Empty string disables upload.'
required: false
type: string
default: ''
browser:
description: 'Browser to use for tests'
required: false
type: string
default: 'chromium'
env:
NAPI_CLI_VERSION: 2.18.4
TURBO_VERSION: 2.8.11
NODE_LTS_VERSION: 20.9.0
# run-tests.js reads `TEST_CONCURRENCY` if no explicit `--concurrency` or `-c`
# argument is provided
TEST_CONCURRENCY: 8
# disable backtrace for test snapshots
RUST_BACKTRACE: 0
TURBO_TEAM: 'vtest314-next-e2e-tests'
TURBO_CACHE: 'remote:rw'
TURBO_TOKEN: ${{ secrets.TURBO_REMOTE_CACHE_TOKEN }}
NEXT_TELEMETRY_DISABLED: 1
# allow not skipping install-native postinstall script if we don't have a binary available already
NEXT_SKIP_NATIVE_POSTINSTALL: ${{ inputs.skipNativeInstall == 'yes' && '1' || '' }}
DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }}
NEXT_JUNIT_TEST_REPORT: 'true'
DD_ENV: 'ci'
# Vercel KV Store for test timings
KV_REST_API_URL: ${{ secrets.KV_REST_API_URL }}
KV_REST_API_TOKEN: ${{ secrets.KV_REST_API_TOKEN }}
NEXT_TEST_JOB: 1
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
VERCEL_TEST_TEAM: vtest314-next-e2e-tests
VERCEL_ADAPTER_TEST_TOKEN: ${{ secrets.VERCEL_ADAPTER_TEST_TOKEN }}
VERCEL_ADAPTER_TEST_TEAM: vtest314-next-adapter-e2e-tests
VERCEL_TURBOPACK_TEST_TOKEN: ${{ secrets.VERCEL_TURBOPACK_TEST_TOKEN }}
VERCEL_TURBOPACK_TEST_TEAM: vtest314-next-turbo-e2e-tests
NEXT_TEST_PREFER_OFFLINE: 1
NEXT_CI_RUNNER: ${{ inputs.runs_on_labels }}
NEXT_TEST_PROXY_ADDRESS: ${{ inputs.overrideProxyAddress || '' }}
# defaults to 256, but we run a lot of tests in parallel, so the limit should be lower
NEXT_TURBOPACK_IO_CONCURRENCY: 64
# Disable warnings from baseline-browser-mapping
# https://github.com/web-platform-dx/baseline-browser-mapping/blob/ec8136ae9e034b332fab991d63a340d2e13b8afc/README.md?plain=1#L34
BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA: 1
jobs:
build:
timeout-minutes: ${{ inputs.timeout_minutes }}
runs-on: ${{ fromJson(inputs.runs_on_labels) }}
defaults:
run:
shell: bash -leo pipefail {0}
outputs:
input_step_key: ${{ steps.var.outputs.input_step_key }}
steps:
# enforce consistent line endings for git on windows
- name: Configure git to use LF endings
if: ${{ contains(fromJson(inputs.runs_on_labels), 'windows') }}
run: |
git config --global core.autocrlf false
git config --global core.eol lf
shell: bash
- name: Check if fnm is installed
id: check-fnm
run: |
if [ -x "$(command -v fnm)" ]; then
echo "fnm found."
echo "found=true" >> $GITHUB_OUTPUT
else
echo "fnm not found."
echo "found=false" >> $GITHUB_OUTPUT
fi
- name: Install fnm
if: steps.check-fnm.outputs.found != 'true'
run: |
curl -fsSL https://fnm.vercel.app/install | bash
export PATH="/home/runner/.local/share/fnm:$PATH"
echo "/home/runner/.local/share/fnm" >> $GITHUB_PATH
fnm env --json | jq -r 'to_entries|map("\(.key)=\(.value|tostring)")|.[]' | xargs -I {} echo "{}" >> $GITHUB_ENV
- name: Normalize input step names into path key
uses: actions/github-script@v7
id: var
with:
script: |
core.setOutput('input_step_key', '${{ inputs.stepName }}'.toLowerCase().replaceAll(/[/.]/g, '-').trim('-'));
- name: Use Node.js ${{ inputs.nodeVersion || env.NODE_LTS_VERSION }} for default shell
run: |
node --version || true
which node || true
# TODO: May be sufficient to just set `$FNM_MULTISHELL_PATH/bin` from `fnm env --json` into GITHUB_PATH
fnm_env=$(fnm env)
# Debug what we're about to eval
echo "$fnm_env"
eval "$fnm_env"
fnm use --install-if-missing ${{ inputs.nodeVersion || env.NODE_LTS_VERSION }}
fnm default ${{ inputs.nodeVersion || env.NODE_LTS_VERSION }}
node --version
which node
NODE_SHELL_PATH=$(dirname "$(which node)")
echo "Adding '$NODE_SHELL_PATH' to GITHUB_PATH"
echo "$NODE_SHELL_PATH" >> "$GITHUB_PATH"
# Debug used Node.js version in a separate step to ensure
# the Node.js version is set for the entire job
- name: Verify Node.js version
run: |
which node
node --version
- name: Prepare corepack
if: ${{ contains(fromJson(inputs.runs_on_labels), 'ubuntu-latest') }}
run: |
npm i -g corepack@0.31
- run: corepack enable
- run: pwd
- run: rm -rf .git
- uses: actions/checkout@v4
with:
fetch-depth: 25
# Cache pnpm store on GitHub-hosted runners (self-hosted runners have their own persistent storage)
- name: Get pnpm store directory
if: ${{ runner.environment == 'github-hosted' }}
id: get-store-path
run: echo STORE_PATH=$(pnpm store path) >> $GITHUB_OUTPUT
- name: Cache pnpm store
if: ${{ runner.environment == 'github-hosted' }}
uses: actions/cache@v4
timeout-minutes: 5
id: cache-pnpm-store
with:
path: ${{ steps.get-store-path.outputs.STORE_PATH }}
key: pnpm-store-v2-${{ hashFiles('pnpm-lock.yaml') }}
# Do not use restore-keys since it leads to indefinite growth of the cache.
# local action -> needs to run after checkout
- name: Install Rust
uses: ./.github/actions/setup-rust
if: ${{ inputs.skipNativeBuild != 'yes' || inputs.needsNextest == 'yes' || inputs.needsRust == 'yes' }}
- name: Install nextest
if: ${{ inputs.needsNextest == 'yes' }}
uses: taiki-e/install-action@nextest
- run: rustc --version
if: ${{ inputs.skipNativeBuild != 'yes' || inputs.needsNextest == 'yes' || inputs.needsRust == 'yes' }}
- run: corepack prepare --activate yarn@1.22.19 && npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}"
- name: Cache on ${{ github.ref_name }}
uses: ijjk/rust-cache@turbo-cache-v1.0.9
if: ${{ inputs.rustCacheKey }}
with:
cache-provider: 'turbo'
save-if: ${{ github.ref_name == 'canary' }}
shared-key: ${{ inputs.rustCacheKey }}-${{ inputs.buildNativeTarget }}-build-${{ inputs.rustBuildProfile }}-${{ hashFiles('.cargo/config.toml') }}
# clean up any previous artifacts to avoid hitting disk space limits
- run: git clean -xdf && rm -rf /tmp/next-repo-*; rm -rf /tmp/next-install-* /tmp/yarn-* /tmp/ncc-cache target
# Configure a git user so that Create Next App can initialize git repos during integration tests.
- name: Set CI git user
run: |
git config --global user.name "vercel-ci-bot"
git config --global user.email "infra+ci@vercel.com"
- run: cargo clean
if: ${{ inputs.skipNativeBuild != 'yes' || inputs.needsNextest == 'yes' || inputs.needsRust == 'yes' }}
# normalize versions before build-native for better cache hits
- run: node scripts/normalize-version-bump.js
name: normalize versions
- run: pnpm dlx turbo@${TURBO_VERSION} run build-native-${{ inputs.rustBuildProfile }} -v --env-mode loose --remote-cache-timeout 90 --summarize -- --target ${{ inputs.buildNativeTarget }}
if: ${{ inputs.skipNativeBuild != 'yes' }}
- name: Upload next-swc artifact
if: ${{ inputs.uploadSwcArtifact == 'yes' }}
uses: actions/upload-artifact@v4
with:
name: next-swc-binary
path: packages/next-swc/native/next-swc.linux-x64-gnu.node
# undo normalize version changes for install/build
- run: git checkout .
if: ${{ inputs.skipInstallBuild != 'yes' }}
- run: pnpm install
if: ${{ inputs.skipInstallBuild != 'yes' }}
- name: Install node-file-trace test dependencies
if: ${{ inputs.needsNextest == 'yes' }}
working-directory: turbopack/crates/turbopack-tracing/tests/node-file-trace
run: pnpm install -r --side-effects-cache false
- run: ANALYZE=1 pnpm build
if: ${{ inputs.skipInstallBuild != 'yes' }}
- run: pnpm playwright install --with-deps ${{ inputs.browser }}
if: ${{ inputs.skipInstallBuild != 'yes' }}
- name: Download pre-built test timings
if: ${{ inputs.testTimingsArtifact != '' }}
uses: actions/download-artifact@v4
with:
name: ${{ inputs.testTimingsArtifact }}
- name: Verify test timings
if: ${{ inputs.testTimingsArtifact != '' }}
run: |
if [ ! -f test-timings.json ]; then
echo "::error::test-timings.json not found"
exit 1
fi
echo "Test timings loaded ($(wc -c < test-timings.json) bytes)"
- name: Fetch test timings via turbo
if: ${{ inputs.testTimingsArtifact == '' }}
run: pnpm dlx turbo@${TURBO_VERSION} run get-test-timings -- --build ${{ github.sha }}
- run: ${{ inputs.afterBuild }}
# defaults.run.shell sets a stronger options (`-leo pipefail`)
# Set this back to github action's weaker defaults:
# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell
#
# We must use a login shell: fnm installation may modify the `.profile`
shell: bash -le {0}
timeout-minutes: ${{ inputs.timeout_minutes }}
- name: Upload test result artifacts
if: ${{ inputs.testReportsArtifactPrefix != '' && inputs.afterBuild && always() }}
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.testReportsArtifactPrefix }}-${{ steps.var.outputs.input_step_key }}
path: test/**/*.results.json
if-no-files-found: ignore
retention-days: 1
# This file messes up the tests because it influences the build root autodetection.
# Jest has a global cache, so PRs that poison the cache can bring down CI
- name: Clean up stray files
if: ${{ always() }}
run: rm -f /tmp/package-lock.json
- name: Upload Turborepo summary
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: turbo-run-summary-${{ steps.var.outputs.input_step_key }}
path: .turbo/runs
if-no-files-found: ignore
- name: Upload bundle analyzer artifacts
uses: actions/upload-artifact@v4
if: ${{ inputs.uploadAnalyzerArtifacts == 'yes' }}
with:
name: webpack bundle analysis stats-${{ steps.var.outputs.input_step_key }}
path: packages/next/dist/compiled/next-server/report.*.html
- name: Upload test report to datadog
if: ${{ inputs.afterBuild && always() && !github.event.pull_request.head.repo.fork }}
run: |
# Add a `test.type` tag to distinguish between turbopack and next.js runs
# Add a `nextjs.test_session.name` tag to help identify the job
if [ -d ./test/test-junit-report ]; then
pnpm dlx @datadog/datadog-ci@2.45.1 junit upload \
--service nextjs \
--tags test.type:nextjs \
--tags test_session.name:"${{ inputs.stepName }}" \
--tags runner.name:"${{ runner.name }}" \
./test/test-junit-report
fi
if [ -d ./test/turbopack-test-junit-report ]; then
pnpm dlx @datadog/datadog-ci@2.45.1 junit upload \
--service nextjs \
--tags test.type:turbopack \
--tags test_session.name:"${{ inputs.stepName }}" \
--tags runner.name:"${{ runner.name }}" \
./test/turbopack-test-junit-report
fi
- name: Upload Playwright Snapshots
uses: actions/upload-artifact@v4
if: ${{ inputs.afterBuild && always() }}
with:
name: test-playwright-snapshots-${{ steps.var.outputs.input_step_key }}
path: |
test/traces
if-no-files-found: ignore
code_freeze .github/workflows/code_freeze.yml
View raw YAML
on:
workflow_dispatch:
inputs:
type:
description: Enable/disable code freeze
required: true
type: choice
options:
- enable
- disable
secrets:
CODE_FREEZE_TOKEN:
required: true
name: Code Freeze
env:
NAPI_CLI_VERSION: 2.18.4
TURBO_VERSION: 2.8.11
NODE_LTS_VERSION: 20
jobs:
start:
runs-on: ubuntu-latest
environment: release-${{ github.event.inputs.releaseType }}
steps:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- run: git clone https://github.com/vercel/next.js.git --depth=1 .
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- run: node ./scripts/code-freeze.js --type ${{ github.event.inputs.type }}
env:
CODE_FREEZE_TOKEN: ${{ secrets.CODE_FREEZE_TOKEN }}
create_release_branch .github/workflows/create_release_branch.yml
View raw YAML
on:
workflow_dispatch:
inputs:
branchName:
description: name of branch to create (next-15-4)
required: true
type: string
tagName:
description: Tag to start the branch from (v15.4.1)
type: string
required: true
secrets:
RELEASE_BOT_GITHUB_TOKEN:
required: true
name: Create Release Branch
env:
NAPI_CLI_VERSION: 2.18.4
TURBO_VERSION: 2.8.11
NODE_LTS_VERSION: 20
jobs:
start:
if: github.repository_owner == 'vercel'
runs-on: ubuntu-latest
env:
NEXT_TELEMETRY_DISABLED: 1
# we build a dev binary for use in CI so skip downloading
# canary next-swc binaries in the monorepo
NEXT_SKIP_NATIVE_POSTINSTALL: 1
steps:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
check-latest: true
- name: Clone Next.js repository
run: git clone https://github.com/vercel/next.js.git --depth=25 --single-branch --branch ${GITHUB_REF_NAME:-canary} .
- name: Check token
run: gh auth status
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
pnpm --version
- id: get-store-path
run: echo STORE_PATH=$(pnpm store path) >> $GITHUB_OUTPUT
- uses: actions/cache@v4
timeout-minutes: 5
id: cache-pnpm-store
with:
path: ${{ steps.get-store-path.outputs.STORE_PATH }}
key: pnpm-store-v2-${{ hashFiles('pnpm-lock.yaml') }}
# Do not use restore-keys since it leads to indefinite growth of the cache.
- run: pnpm install
- run: node ./scripts/create-release-branch.js --branch-name ${{ github.event.inputs.branchName }} --tag-name ${{ github.event.inputs.tagName }}
env:
RELEASE_BOT_GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
deploy_docs perms .github/workflows/deploy_docs.yml
View raw YAML
name: docs-deploy
on:
pull_request:
paths:
- 'apps/docs/**'
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
env:
NODE_LTS_VERSION: 20
jobs:
deploy:
name: Deploy docs to Vercel
runs-on: ubuntu-latest
env:
VERCEL_API_TOKEN: ${{ secrets.VERCEL_API_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
fetch-depth: 25
- name: Install Vercel CLI
run: npm i -g vercel@latest
- name: Deploy docs
id: deploy
run: |
URL=$(bash scripts/deploy-docs.sh)
echo "url=$URL" >> $GITHUB_OUTPUT
echo "Deployed to $URL"
env:
DEPLOY_ENVIRONMENT: preview
# - name: Comment on PR with deployment URL
# if: ${{ github.event_name == 'pull_request' }}
# uses: actions/github-script@v7
# with:
# script: |
# const pr = github.context.payload.pull_request?.number
# const url = '${{ steps.deploy.outputs.url }}'
# if (!pr || !url) return
# await github.rest.issues.createComment({
# owner: context.repo.owner,
# repo: context.repo.repo,
# issue_number: pr,
# body: `Docs deployed: ${url}`
# })
graphite_ci_optimizer .github/workflows/graphite_ci_optimizer.yml
View raw YAML
# Avoid running the full CI on mid-stack PRs: https://graphite.dev/docs/stacking-and-ci
#
# We still run some high-signal low-cost jobs (e.g. lint, unit tests) on these mid-stack PRs, just
# not anything slow (e.g. integration tests).
#
# Because we don't use Graphite's CI batching, full CI will still run on every PR individually
# before it merges. The goal is just to avoid wasting CI capacity when frequently rebasing large
# stacks.
#
# This can by bypassed by labeling a PR with 'CI Bypass Graphite Optimization', and manually
# re-running CI.
name: Graphite CI Optimizer
on:
workflow_call:
outputs:
skip:
description: "'true' if expensive CI checks should be skipped, 'false' otherwise."
value: ${{ jobs.optimize-ci.outputs.skip }}
secrets:
GRAPHITE_TOKEN:
description: 'The Graphite CI optimization secret'
# secrets are not available in forks, check-skip will just fail-open with a warning
required: false
env:
# FYI, if you add this label, you must *push* to the repository again to trigger a new event. Just
# re-running in the GitHub actions UI won't work, as it will re-use the old event with the old
# labels.
HAS_BYPASS_LABEL: |-
${{
github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'CI Bypass Graphite Optimization')
}}
jobs:
optimize-ci:
name: Graphite CI Optimizer
runs-on: ubuntu-latest
outputs:
skip: ${{ env.HAS_BYPASS_LABEL == 'false' && steps.check-skip.outputs.skip == 'true' }}
steps:
- name: Optimize CI
id: check-skip
uses: withgraphite/graphite-ci-action@main
with:
graphite_token: ${{ secrets.GRAPHITE_TOKEN }}
- name: Debug Output
run: |
echo 'github.event_name: ${{ github.event_name }}'
echo "secrets.GRAPHITE_TOKEN != '': ${{ secrets.GRAPHITE_TOKEN != '' }}"
echo 'env.HAS_BYPASS_LABEL: ${{ env.HAS_BYPASS_LABEL }}'
echo 'steps.check-skip.outputs.skip: ${{ steps.check-skip.outputs.skip }}'
integration_tests_reusable matrix .github/workflows/integration_tests_reusable.yml
View raw YAML
name: Integration Tests Reusable
on:
workflow_call:
inputs:
name:
description: A unique identifer used for uploaded assets
type: string
test_type:
description: '"development" or "production"'
required: true
type: string
run_before_test:
description: >
Bash code to run before executing the test (e.g. setting environment
variables). Runs in the same step as the test.
type: string
default: ''
e2e_groups:
description: >
Size of the matrix used for running e2e tests (controls parallelism)
type: number
default: 6
integration_groups:
description: >
Size of the matrix used for running legacy integration tests (controls
parallelism)
type: number
default: 6
e2e_timeout_minutes:
type: number
default: 30
integration_timeout_minutes:
type: number
default: 30
num_retries:
type: number
default: 2
jobs:
# First, build Next.js to execute across tests.
build-next:
name: build-next
uses: ./.github/workflows/build_reusable.yml
with:
skipNativeBuild: yes
stepName: build-next
secrets: inherit
build-native:
name: build-native
uses: ./.github/workflows/build_reusable.yml
with:
skipInstallBuild: yes
stepName: build-native
secrets: inherit
generate-matrices:
runs-on: [self-hosted, linux, x64, metal]
steps:
- id: out
run: |
printf 'e2e=[%s]\n' \
"$(seq -s, 1 ${{ inputs.e2e_groups }})" | \
tee -a "$GITHUB_OUTPUT"
printf 'integration=[%s]\n' \
"$(seq -s, 1 ${{ inputs.integration_groups }})" | \
tee -a "$GITHUB_OUTPUT"
outputs:
e2e: ${{ steps.out.outputs.e2e }}
integration: ${{ steps.out.outputs.integration }}
# Actual test scheduling. These jobs mimic the normal test jobs.
# Refer build_and_test.yml for more details.
#
# We run tests in two parts. Legacy integration tests are run separately:
# https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#test-types-in-nextjs
test-e2e:
# Name must match `integrationTestJobs` in
# `./.github/actions/next-integration-stat`
name: >-
Next.js integration test (E2E and ${{ inputs.test_type }})
(${{ matrix.group }}/${{ inputs.e2e_groups }})
needs: [build-next, build-native, generate-matrices]
strategy:
fail-fast: false
matrix:
group: ${{ fromJSON(needs.generate-matrices.outputs.e2e) }}
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
# e2e and ${{ inputs.test_type }} tests with `node run-tests.js`
export NEXT_TEST_MODE=${{
inputs.test_type == 'development' && 'dev' || 'start'
}}
export NEXT_TEST_EMIT_ALL_OUTPUT=1
${{ inputs.run_before_test }}
node run-tests.js \
--group ${{ matrix.group }}/${{ inputs.e2e_groups }} \
--retries ${{ inputs.num_retries }} \
--type ${{ inputs.test_type }}
stepName: test-${{ inputs.name }}-${{ matrix.group }}
timeout_minutes: ${{ inputs.e2e_timeout_minutes }}
secrets: inherit
test-integration:
# Name must match `integrationTestJobs` in
# `./.github/actions/next-integration-stat`
name: >-
Next.js integration test (Integration)
(${{ matrix.group }}/${{ inputs.e2e_groups }})
needs: [build-next, build-native, generate-matrices]
strategy:
fail-fast: false
matrix:
group: ${{ fromJSON(needs.generate-matrices.outputs.integration) }}
uses: ./.github/workflows/build_reusable.yml
with:
nodeVersion: 20.9.0
afterBuild: |
# legacy integration tests with `node run-tests.js`
# HACK: Despite the name, these environment variables are just used to
# gate tests, so they're applicable to both turbopack and rspack tests
export ${{
inputs.test_type == 'development' &&
'TURBOPACK_DEV=1' ||
'TURBOPACK_BUILD=1'
}}
export NEXT_TEST_EMIT_ALL_OUTPUT=1
${{ inputs.run_before_test }}
node run-tests.js \
--group ${{ matrix.group }}/${{ inputs.integration_groups }} \
--retries ${{ inputs.num_retries }} \
--type integration
stepName: test-${{ inputs.name }}-integration-${{ matrix.group }}
timeout_minutes: ${{ inputs.integration_timeout_minutes }}
secrets: inherit
# Collect integration test results from execute_tests,
# Store it as github artifact for next step to consume.
collect_nextjs_development_integration_stat:
needs: [test-e2e, test-integration]
name: Next.js integration test development status report
runs-on: [self-hosted, linux, x64, metal]
if: always()
permissions:
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Collect integration test stat
uses: ./.github/actions/next-integration-stat
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Store artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ inputs.name }}
path: |
nextjs-test-results.json
failed-test-path-list.json
passed-test-path-list.json
issue_lock perms .github/workflows/issue_lock.yml
View raw YAML
name: 'Lock Threads'
on:
schedule:
# This runs twice a day: https://crontab.guru/#0_0,12_*_*_*
- cron: '0 0,12 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
action:
runs-on: ubuntu-latest
if: github.repository_owner == 'vercel'
steps:
- uses: dessant/lock-threads@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
add-issue-labels: 'locked'
add-pr-labels: 'locked'
issue-inactive-days: 14
issue-comment: 'This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.'
pr-inactive-days: 14
log-output: true
issue_stale .github/workflows/issue_stale.yml
View raw YAML
name: 'Stale issue handler'
on:
workflow_dispatch:
schedule:
# This runs every day 20 minutes before midnight: https://crontab.guru/#40_23_*_*_*
- cron: '40 23 * * *'
jobs:
stale:
runs-on: ubuntu-latest
if: github.repository_owner == 'vercel'
steps:
- uses: actions/stale@v9
id: issue-stale
name: 'Mark stale issues, close stale issues'
with:
repo-token: ${{ secrets.STALE_TOKEN }}
ascending: true
days-before-issue-close: 7
days-before-issue-stale: 545 # issues with no activity in over ~1.5 years
days-before-pr-close: -1
days-before-pr-stale: -1
remove-issue-stale-when-updated: true
stale-issue-label: 'stale'
labels-to-add-when-unstale: 'not stale'
stale-issue-message: 'This issue has been automatically marked as stale due to inactivity. It will be closed in 7 days unless there’s further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you.'
close-issue-message: 'This issue has been automatically closed due to inactivity. If you’re still experiencing a similar problem or have additional details to share, please open a new issue following our current issue template. Your updated report helps us investigate and address concerns more efficiently. Thank you for your understanding!'
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
- uses: actions/stale@v9
id: stale-no-repro
name: 'Close stale issues with no reproduction'
with:
repo-token: ${{ secrets.STALE_TOKEN }}
any-of-issue-labels: 'please add a complete reproduction'
close-issue-message: 'This issue has been automatically closed due to 2 days of inactivity and the absence of a complete reproduction. If you believe this was done in error, please leave a comment. If you are experiencing a similar issue, consider opening a new issue with a complete reproduction. Thank you.'
days-before-issue-close: 2
days-before-issue-stale: 1
days-before-pr-close: -1
days-before-pr-stale: -1
remove-issue-stale-when-updated: true
stale-issue-label: 'stale'
labels-to-add-when-unstale: 'not stale'
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
- uses: actions/stale@v9
id: stale-simple-repro
name: 'Close issues with no simple repro'
with:
repo-token: ${{ secrets.STALE_TOKEN }}
any-of-issue-labels: 'please simplify reproduction'
close-issue-message: 'This issue has been automatically closed due to 14 days of inactivity and the absence of a simple reproduction for investigation. If you believe this was done in error, please leave a comment. If you are experiencing a similar issue, consider opening a new issue with a simple reproduction. Thank you.'
days-before-issue-close: 14
days-before-issue-stale: 1
days-before-pr-close: -1
days-before-pr-stale: -1
remove-issue-stale-when-updated: true
stale-issue-label: 'stale'
labels-to-add-when-unstale: 'not stale'
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
- uses: actions/stale@v9
id: stale-no-canary
name: 'Close issues not verified on canary'
with:
repo-token: ${{ secrets.STALE_TOKEN }}
any-of-issue-labels: 'please verify canary'
close-issue-message: 'This issue has been automatically closed due to 14 days of inactivity and the absence of testing against next@canary. If you believe this was done in error, please leave a comment. If you are experiencing a similar issue, consider opening a new issue with a reproduction. Thank you.'
days-before-issue-close: 14
days-before-issue-stale: 1
days-before-pr-close: -1
days-before-pr-stale: -1
remove-issue-stale-when-updated: true
stale-issue-label: 'stale'
labels-to-add-when-unstale: 'not stale'
operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
issue_wrong_template .github/workflows/issue_wrong_template.yml
View raw YAML
name: 'Close issues using the wrong issue template'
on:
issues:
types: [labeled]
jobs:
close:
if: github.event.label.name == 'please use the correct issue template'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: 'Close issues using the wrong issue template'
run: node ./.github/actions/next-repo-actions/dist/wrong-issue-template/index.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
popular .github/workflows/popular.yml
View raw YAML
name: Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days
on:
schedule:
- cron: '0 10 * * 1' # Every Monday at 10AM UTC (6AM EST)
workflow_dispatch:
jobs:
run:
if: github.repository_owner == 'vercel'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: 'Issues: Send notification to Slack'
run: node ./.github/actions/next-repo-actions/dist/issues/index.mjs
continue-on-error: true
- name: 'PRs: Send notification to Slack'
run: node ./.github/actions/next-repo-actions/dist/prs/index.js
continue-on-error: true
- name: 'Feature requests: Send notification to Slack'
run: node ./.github/actions/next-repo-actions/dist/feature-requests/index.mjs
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
pull_request_stats matrix .github/workflows/pull_request_stats.yml
View raw YAML
on:
pull_request:
types: [opened, synchronize]
push:
branches:
- canary
name: Generate Stats
concurrency:
# Keep cancel-on-update behavior for PRs, but allow canary pushes to run independently.
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
NAPI_CLI_VERSION: 2.18.4
TURBO_VERSION: 2.8.11
NODE_LTS_VERSION: 20
TEST_CONCURRENCY: 6
TURBO_TEAM: 'vercel'
TURBO_CACHE: 'remote:rw'
NEXT_TELEMETRY_DISABLED: 1
# we build a dev binary for use in CI so skip downloading
# canary next-swc binaries in the monorepo
NEXT_SKIP_NATIVE_POSTINSTALL: 1
# Vercel KV Store for test timings
KV_REST_API_URL: ${{ secrets.KV_REST_API_URL }}
KV_REST_API_TOKEN: ${{ secrets.KV_REST_API_TOKEN }}
NEXT_TEST_JOB: 1
NEXT_DISABLE_SWC_WASM: 1
jobs:
build:
uses: ./.github/workflows/build_reusable.yml
secrets: inherit
with:
stepName: 'generate-pull-request-stats'
uploadSwcArtifact: 'yes'
uploadAnalyzerArtifacts: 'yes'
stats:
name: Stats (${{ matrix.bundler }})
needs: build
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
bundler: [webpack, turbopack]
runs-on:
- 'self-hosted'
- 'linux'
- 'x64'
- 'metal'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
- name: Check non-docs only change
run: echo "DOCS_CHANGE<<EOF" >> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.mjs --not --type docs --exec echo 'nope')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT
id: docs-change
- uses: actions/download-artifact@v4
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
with:
name: next-swc-binary
path: packages/next-swc/native
- run: cp -r packages/next-swc/native .github/actions/next-stats-action/native
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
- uses: ./.github/actions/next-stats-action
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
with:
bundler: ${{ matrix.bundler }}
env:
PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }}
TURBO_TEAM: ${{ env.TURBO_TEAM }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_CACHE: ${{ env.TURBO_CACHE }}
- name: Upload stats results
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
uses: actions/upload-artifact@v4
with:
name: pr-stats-${{ matrix.bundler }}
path: pr-stats-${{ matrix.bundler }}.json
retention-days: 1
stats-aggregate:
name: Aggregate Stats
needs: stats
if: always() && needs.stats.result != 'cancelled'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
- name: Check non-docs only change
run: echo "DOCS_CHANGE<<EOF" >> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.mjs --not --type docs --exec echo 'nope')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT
id: docs-change
- name: Download all stats artifacts
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
uses: actions/download-artifact@v4
with:
pattern: pr-stats-*
path: stats-results
merge-multiple: true
- name: Setup Node.js
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
- name: Install dependencies
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
working-directory: .github/actions/next-stats-action
run: npm install
- name: Aggregate and post results
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
working-directory: .github/actions/next-stats-action
run: node src/aggregate-results.js ${{ github.workspace }}/stats-results
env:
PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }}
KV_REST_API_URL: ${{ secrets.KV_REST_API_URL }}
KV_REST_API_TOKEN: ${{ secrets.KV_REST_API_TOKEN }}
release-next-rspack .github/workflows/release-next-rspack.yml
View raw YAML
name: Release next-rspack bindings
on:
workflow_dispatch:
inputs:
dry-run:
description: 'Run in dry-run mode (no actual publishing)'
required: false
default: false
type: boolean
npm-name:
description: 'NPM package name to publish'
required: false
default: '@next/rspack-core'
type: string
npm-tag:
description: 'NPM tag for publishing'
required: false
default: 'latest'
type: choice
options:
- latest
- alpha
- beta
- canary
env:
DEBUG: napi:*
jobs:
build:
name: Build
uses: rspack-contrib/rspack-toolchain/.github/workflows/build.yml@f69dc04fcae6b38d97b87acef448ed7a285b01cc
with:
package-json-path: rspack/crates/binding/package.json
napi-build-command: pnpm build --release
working-directory: rspack
release:
runs-on: ubuntu-latest
environment: npm
name: Release
permissions:
contents: write
id-token: write
needs: [build]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Display release mode
run: |
echo "🚀 Release Configuration:"
echo " - Dry-run mode: ${{ inputs.dry-run }}"
echo " - NPM name: ${{ inputs.npm-name || '@next/rspack-core' }}"
echo " - NPM tag: ${{ inputs.npm-tag || 'latest' }}"
if [ "${{ inputs.dry-run }}" == "true" ]; then
echo " - ⚠️ This is a DRY RUN - no packages will be published"
else
echo " - 📦 This will PUBLISH packages to npm"
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Enable corepack
run: corepack enable
- name: Setup pnpm
run: corepack prepare
- name: Change npm name
run: pnpm run change-npm-name "${{ inputs.npm-name }}"
working-directory: ./rspack
- name: Cache pnpm dependencies
uses: actions/cache@v3
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ runner.arch }}-pnpm-v2-${{ hashFiles('**/pnpm-lock.yaml') }}
# Do not use restore-keys since it leads to indefinite growth of the cache.
- name: Install dependencies
run: pnpm install
working-directory: ./rspack
- name: Get NAPI info
id: napi-info
uses: rspack-contrib/rspack-toolchain/get-napi-info@f69dc04fcae6b38d97b87acef448ed7a285b01cc
with:
package-json-path: rspack/crates/binding/package.json
- name: Download rspack binding
uses: rspack-contrib/rspack-toolchain/download-rspack-binding@f69dc04fcae6b38d97b87acef448ed7a285b01cc
with:
path: ${{ steps.napi-info.outputs.binding-directory }}/artifacts
- name: List artifacts
run: ls -R artifacts
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
- name: Create npm dirs
run: pnpm napi create-npm-dirs
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
- name: Move artifacts
run: pnpm napi artifacts
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
- name: List npm dirs
run: ls -R npm
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
- name: Create npm token
run: |
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }}
- name: Release npm binding packages
run: |
npm config set access public
npm config set provenance true
pnpm napi pre-publish --no-gh-release -t npm ${{ inputs.dry-run && '--dry-run' || '' }}
working-directory: ${{ steps.napi-info.outputs.binding-directory }}
- name: Release npm packages
run: |
pnpm publish -r --tag ${{ inputs.npm-tag }} --no-git-checks --provenance --access public ${{ inputs.dry-run && '--dry-run' || '' }}
working-directory: ./rspack
retry_deploy_test perms .github/workflows/retry_deploy_test.yml
View raw YAML
name: retry-deploy-tests
on:
workflow_run:
workflows: ['test-e2e-deploy-release']
types:
- completed
env:
SLACK_WEBHOOK_URL: ${{ secrets.BROKEN_DEPLOY_SLACK_WEBHOOK_URL }}
permissions:
actions: write
jobs:
retry-on-failure:
name: retry failed jobs
# Retry the test-e2e-deploy-release workflow once
if: >-
${{
(
github.event.workflow_run.event == 'release' ||
github.event.workflow_run.display_title == 'test-e2e-deploy canary'
) &&
github.event.workflow_run.conclusion == 'failure' &&
github.repository == 'vercel/next.js' &&
github.event.workflow_run.run_attempt < 2
}}
runs-on: ubuntu-latest
steps:
- name: send retry request to GitHub API
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/rerun-failed-jobs
report-failure:
name: report failure to slack
# Report the failure to Slack if the test-e2e-deploy-release workflow has failed 2 times
if: >-
${{
(
github.event.workflow_run.event == 'release' ||
github.event.workflow_run.display_title == 'test-e2e-deploy canary'
) &&
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.run_attempt >= 2 &&
!github.event.workflow_run.head_repository.fork
}}
runs-on: ubuntu-latest
steps:
- name: send webhook
uses: slackapi/slack-github-action@v1.25.0
with:
payload: |
{
"commit_title": ${{ toJSON(github.event.workflow_run.display_title) }},
"commit_url": "github.com/${{ github.repository }}/commit/${{ github.event.workflow_run.head_sha }}",
"workflow_run_url": "github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/attempts/${{ github.event.workflow_run.run_attempt }}",
"workflow_branch": ${{ toJSON(github.event.workflow_run.head_branch) }}
}
env:
SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }}
retry_test perms .github/workflows/retry_test.yml
View raw YAML
name: retry-tests
on:
workflow_run:
# Make sure that required_job_conclusion knows what the name of the required
# job is in each workflow.
workflows: ['build-and-test', 'build-and-deploy']
branches: [canary]
types:
- completed
env:
SLACK_WEBHOOK_URL: ${{ secrets.BROKEN_CANARY_SLACK_WEBHOOK_URL }}
permissions:
actions: write
jobs:
retry-on-failure:
name: retry failed jobs
# Retry the build-and-test workflow up to 2 times
if: >-
${{
github.event.workflow_run.conclusion == 'failure' &&
github.repository == 'vercel/next.js' &&
github.event.workflow_run.run_attempt < 3
}}
runs-on: ubuntu-latest
steps:
- name: Check conclusion of required job
id: required_job_conclusion
uses: actions/github-script@v7
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
// See build-and-test.yml and build-and-deploy.yml for the required job names
const requiredJobName = {
'build-and-test': 'thank you, next',
'build-and-deploy': 'thank you, build',
}[context.payload.workflow_run.name]
async function rerunIfRequiredNotSuccessful() {
const result = await github.paginate.iterator(
'GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs',
{
attempt_number: context.payload.workflow_run.run_attempt,
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
}
)
for await (const { data: jobs } of result) {
for (const job of jobs) {
if (job.name === requiredJobName) {
console.log(
"Using conclusion '%s' from %s",
job.conclusion,
job.html_url
)
return job.conclusion
}
}
}
console.log("Couldn't find job with name '%s'", requiredJobName)
return null
}
return await rerunIfRequiredNotSuccessful()
- name: send retry request to GitHub API
if: ${{ steps.required_job_conclusion.outputs.result != '"success"' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/rerun-failed-jobs
report-failure:
name: report failure to slack
# Report the failure to Slack if the build-and-test workflow has failed 3 times
# build-and-deploy is not retried so we always report it
if: >-
${{
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.run_attempt >= 3 &&
!github.event.workflow_run.head_repository.fork
}}
runs-on: ubuntu-latest
steps:
- name: Check conclusion of required job
id: required_job_conclusion
uses: actions/github-script@v7
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
// See build-and-test.yml and build-and-deploy.yml for the required job names
const requiredJobName = {
'build-and-test': 'thank you, next',
'build-and-deploy': 'thank you, build',
}[context.payload.workflow_run.name]
async function rerunIfRequiredNotSuccessful() {
const result = await github.paginate.iterator(
'GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs',
{
attempt_number: context.payload.workflow_run.run_attempt,
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
}
)
for await (const { data: jobs } of result) {
for (const job of jobs) {
if (job.name === requiredJobName) {
console.log(
"Using conclusion '%s' from %s",
job.conclusion,
job.html_url
)
return job.conclusion
}
}
}
console.log("Couldn't find job with name '%s'", requiredJobName)
return null
}
return await rerunIfRequiredNotSuccessful()
- name: send webhook
if: ${{ steps.required_job_conclusion.outputs.result != '"success"' }}
uses: slackapi/slack-github-action@v1.25.0
with:
# These urls are intentionally missing the protocol,
# allowing them to be transformed into actual links in the Slack workflow
# (through slightly hacky means).
payload: |
{
"commit_title": ${{ toJSON(github.event.workflow_run.display_title) }},
"commit_url": "github.com/${{ github.repository }}/commit/${{ github.event.workflow_run.head_sha }}",
"workflow_run_url": "github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/attempts/${{ github.event.workflow_run.run_attempt }}"
}
env:
SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }}
rspack-nextjs-build-integration-tests .github/workflows/rspack-nextjs-build-integration-tests.yml
View raw YAML
name: Rspack Next.js production integration tests
on:
schedule:
# Run an hour earlier than the turbopack tests, so we don't overwhelm the CI
- cron: '0 5 * * *'
workflow_dispatch: {}
jobs:
test-dev:
name: Rspack integration tests
uses: ./.github/workflows/integration_tests_reusable.yml
with:
name: rspack-production
test_type: production
run_before_test: |
export NEXT_RSPACK=1 NEXT_TEST_USE_RSPACK=1 \
# Failing tests take longer (due to timeouts and retries). Since we have
# many failing tests, we need smaller groups and longer timeouts, in case
# a group gets stuck with a cluster of failing tests.
e2e_groups: 12
integration_groups: 12
e2e_timeout_minutes: 90
integration_timeout_minutes: 90
secrets: inherit
rspack-nextjs-dev-integration-tests .github/workflows/rspack-nextjs-dev-integration-tests.yml
View raw YAML
name: Rspack Next.js development integration tests
on:
schedule:
# Run an hour earlier than the turbopack tests, so we don't overwhelm the CI
- cron: '0 5 * * *'
workflow_dispatch: {}
jobs:
test-dev:
name: Rspack integration tests
uses: ./.github/workflows/integration_tests_reusable.yml
with:
name: rspack-development
test_type: development
run_before_test: |
export NEXT_RSPACK=1 NEXT_TEST_USE_RSPACK=1
# Failing tests take longer (due to timeouts and retries). Since we have
# many failing tests, we need smaller groups and longer timeouts, in case
# a group gets stuck with a cluster of failing tests.
e2e_groups: 16
integration_groups: 16
e2e_timeout_minutes: 90
integration_timeout_minutes: 90
secrets: inherit
rspack-update-tests-manifest .github/workflows/rspack-update-tests-manifest.yml
View raw YAML
# A recurring workflow which updates the passing/failing/skipped integration tests for Turbopack.
name: Update Rspack test manifest
on:
schedule:
# Every day at 7AM https://crontab.guru/#0_7_*_*_*
- cron: '0 7 * * *'
workflow_dispatch:
jobs:
update_dev_manifest:
name: Update and upload Rspack development test manifest
if: github.repository_owner == 'vercel'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Commits made with the default `GITHUB_TOKEN` won't trigger workflows.
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow
token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: Install dependencies
shell: bash
run: pnpm i
- name: Create Pull Request
shell: bash
run: node scripts/automated-update-workflow.js
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
BRANCH_NAME: rspack-manifest
# We need to use `--override` for rspack (but not for turbopack).
# We don't currently have any CI running on every PR, so it's quite
# possible for us to regress on tests. We need to skip the
# only-promote-to-passing merge logic.
SCRIPT: test/update-bundler-manifest.js --bundler rspack --test-suite dev --override
PR_TITLE: Update Rspack development test manifest
PR_BODY: This auto-generated PR updates the development integration test manifest used when testing Rspack.
update_build_manifest:
name: Update and upload Rspack production test manifest
if: github.repository_owner == 'vercel'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Commits made with the default `GITHUB_TOKEN` won't trigger workflows.
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow
token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: Install dependencies
shell: bash
run: pnpm i
- name: Create Pull Request
shell: bash
run: node scripts/automated-update-workflow.js
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
BRANCH_NAME: rspack-manifest
SCRIPT: test/update-bundler-manifest.js --bundler rspack --test-suite build --override
PR_TITLE: Update Rspack production test manifest
PR_BODY: This auto-generated PR updates the production integration test manifest used when testing Rspack.
setup-nextjs-build .github/workflows/setup-nextjs-build.yml
View raw YAML
# Reusable workflow to setup next.js integration test environment.
name: Setup Next.js
on:
workflow_call:
inputs:
# Allow to specify Next.js version to run integration test against.
# If not specified, will use latest release version including canary.
version:
type: string
nodeVersion:
required: false
description: 'version of Node.js to use'
type: string
jobs:
build_nextjs:
name: Build Next.js for the turbopack integration test
runs-on:
- 'self-hosted'
- 'linux'
- 'x64'
- 'metal'
outputs:
output1: ${{ steps.build-next-swc-turbopack-patch.outputs.success }}
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.nodeVersion || env.NODE_LTS_VERSION }}
check-latest: true
- name: Get number of CPU cores
uses: SimenB/github-actions-cpu-cores@v2
id: cpu-cores
- name: 'Setup Rust toolchain'
uses: dtolnay/rust-toolchain@stable
- name: Display runner information
run: echo runner cpu count ${{ steps.cpu-cores.outputs.count }}
- name: Find Next.js latest release version
env:
GH_TOKEN: ${{ github.token }}
run: |
# Grab the latest release version from next.js repo, including prelease. `/releases/latest` will only return latest stable release.
echo NEXJS_LATEST_VERSION=$(gh release --repo vercel/next.js --limit 1 list | sed -n 1p | awk '{print $1}') >> $GITHUB_ENV
- name: Set Next.js release version
run: |
echo "NEXTJS_VERSION=${{ inputs.version != '' && inputs.version || env.NEXJS_LATEST_VERSION }}" >> $GITHUB_ENV
- name: Print Next.js release version to checkout
run: echo "Checking out Next.js ${{ env.NEXTJS_VERSION }}"
- name: Checkout Next.js
uses: actions/checkout@v4
with:
repository: vercel/next.js
ref: ${{ env.NEXTJS_VERSION }}
- name: Checkout failed test lists
uses: actions/checkout@v4
with:
repository: vercel/turbo
ref: nextjs-integration-test-data
path: integration-test-data
- name: Download binary
uses: actions/download-artifact@v4
with:
path: artifacts
- uses: actions/cache/restore@v3
id: restore-build
with:
path: |
./*
key: ${{ inputs.version }}-${{ github.sha }}
- name: Install dependencies
run: |
wget https://github.com/sharkdp/hyperfine/releases/download/v1.16.1/hyperfine_1.16.1_amd64.deb
sudo dpkg -i hyperfine_1.16.1_amd64.deb
npm i -g corepack@0.31
corepack enable
pnpm install --loglevel error
- name: Build next-swc
run: |
hyperfine --min-runs 1 --show-output 'pnpm run --filter=@next/swc build-native --features plugin --release'
echo "Successfully built next-swc with published turbopack"
- name: Build next.js
run: |
pnpm run build
strip packages/next-swc/native/next-swc.*.node
ls -al packages/next-swc/native
# Reduce the size of the cache bit
cd packages/next-swc && cargo clean && cd ../../
echo NEXT_SWC_FILESIZE: $(stat -c %s packages/next-swc/native/next-swc.linux-x64-gnu.node)
node -e "console.log('Host', require('os').arch(), require('os').platform())"
# If input version is published release, detect version by running next.js build.
- name: Detects Next.js build version
run: |
# This is being used in github action to collect test results. Do not change it, or should update ./.github/actions/next-integration-test to match.
docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c 'curl https://install-node.vercel.app/v16 | FORCE=1 bash && cd /work && echo RUNNING NEXTJS VERSION: $(packages/next/dist/bin/next --version) && ls -al packages/next-swc/native && node -e "console.log(\"Container\", require(\"os\").arch(), require(\"os\").platform())"'
- name: Temporary test skip
run: |
rm -rf test/integration/jsconfig-paths/test/index.test.js
# Once build completes, creates a cache of the build output
# so subsequent job to actually execute tests can reuse it.
# Note that we do not use upload / download artifacts for this -
# it is too heavyweight for the purpose since we do not need to persist
# the cache across multiple runs.
- name: Store next.js build cache with next-swc
uses: actions/cache/save@v3
id: cache-build
with:
path: |
./*
key: ${{ inputs.version }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt}}-${{ github.run_number }}
test-turbopack-rust-bench-test .github/workflows/test-turbopack-rust-bench-test.yml
View raw YAML
name: Turbopack Rust testing benchmarks
on:
workflow_call:
inputs:
runner:
type: string
default: '["self-hosted", "linux", "x64", "metal"]'
os:
type: string
default: 'linux'
all:
type: boolean
default: false
env:
TURBOPACK_BENCH_COUNTS: '100'
TURBOPACK_BENCH_PROGRESS: '1'
NODE_LTS_VERSION: 20
jobs:
test:
name: Test
runs-on: ${{ fromJSON(inputs.runner) }}
steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
if: inputs.os == 'windows'
- name: Checkout
uses: actions/checkout@v3
- name: Setup Rust
uses: ./.github/actions/setup-rust
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- run: corepack enable
# We need to install the dependencies for the benchmark apps
- run: pnpm install
working-directory: turbopack/benchmark-apps
- name: Build benchmarks for tests
timeout-minutes: 120
run: |
cargo test --benches --workspace --release --no-fail-fast --exclude turbopack-bench --exclude next-napi-bindings --no-run
- name: Run cargo test on benchmarks
timeout-minutes: 120
run: |
cargo test --benches --workspace --release --no-fail-fast --exclude turbopack-bench --exclude next-napi-bindings
- name: Build benchmarks for tests for other bundlers
if: inputs.all
timeout-minutes: 120
run: |
cargo test --benches --release -p turbopack-bench --no-run
- name: Run cargo test on benchmarks for other bundlers
if: inputs.all
timeout-minutes: 120
run: |
cargo test --benches --release -p turbopack-bench
test_e2e_deploy_release matrix .github/workflows/test_e2e_deploy_release.yml
View raw YAML
name: test-e2e-deploy-release
on:
# run on every release/prerelease
release:
types: [published]
# allow triggering manually as well
workflow_dispatch:
inputs:
nextVersion:
description: canary or custom tarball URL
default: canary
type: string
vercelCliVersion:
description: Version of Vercel CLI to use
default: 'vercel@latest'
type: string
overrideProxyAddress:
description: Override the proxy address to use for the test
default: ''
type: string
deployScriptPath:
description: Custom deploy script path (NEXT_TEST_DEPLOY_SCRIPT_PATH)
default: ''
type: string
deployLogsScriptPath:
description: Custom deploy logs script path (NEXT_TEST_DEPLOY_LOGS_SCRIPT_PATH)
default: ''
type: string
cleanupScriptPath:
description: Custom cleanup script path (NEXT_TEST_CLEANUP_SCRIPT_PATH)
default: ''
type: string
env:
DD_ENV: 'ci'
DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }}
VERCEL_TEST_TEAM: vtest314-next-e2e-tests
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
VERCEL_ADAPTER_TEST_TEAM: vtest314-next-adapter-e2e-tests
VERCEL_ADAPTER_TEST_TOKEN: ${{ secrets.VERCEL_ADAPTER_TEST_TOKEN }}
VERCEL_TURBOPACK_TEST_TEAM: vtest314-next-turbo-e2e-tests
VERCEL_TURBOPACK_TEST_TOKEN: ${{ secrets.VERCEL_TURBOPACK_TEST_TOKEN }}
run-name: test-e2e-deploy ${{ inputs.nextVersion || (github.event_name == 'release' && github.event.release.tag_name) || 'canary' }}
jobs:
setup:
runs-on: ubuntu-latest
if: github.repository_owner == 'vercel'
outputs:
next-version: ${{ steps.version.outputs.value }}
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup pnpm
run: |
npm i -g corepack@0.31
corepack enable
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 25
- id: nextPackageInfo
name: Get `next` package info
run: |
cd packages/next
{
echo 'value<<EOF'
cat package.json
echo EOF
} >> "$GITHUB_OUTPUT"
- id: version
name: Extract `next` version
run: echo 'value=${{ fromJson(steps.nextPackageInfo.outputs.value).version }}' >> "$GITHUB_OUTPUT"
- name: Install dependencies
run: pnpm install
- name: Fetch test timings
run: node run-tests.js --timings --write-timings -g 1/1
continue-on-error: true
env:
KV_REST_API_URL: ${{ secrets.KV_REST_API_URL }}
KV_REST_API_TOKEN: ${{ secrets.KV_REST_API_TOKEN }}
- name: Ensure test timings file exists
run: |
if [ ! -f test-timings.json ]; then
echo "No timings fetched, creating empty timings file"
echo '{}' > test-timings.json
fi
- name: Upload test timings
uses: actions/upload-artifact@v4
with:
name: test-timings
path: test-timings.json
retention-days: 1
if-no-files-found: error
test-deploy-webpack:
name: Run Deploy Tests (Webpack)
needs: setup
if: ${{ github.event.inputs.deployScriptPath == '' }}
uses: ./.github/workflows/build_reusable.yml
secrets: inherit
strategy:
fail-fast: false
matrix:
group: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8]
with:
afterBuild: |
npm i -g vercel@latest && \
NEXT_E2E_TEST_TIMEOUT=240000 \
NEXT_TEST_MODE=deploy \
IS_WEBPACK_TEST=1 \
NEXT_EXTERNAL_TESTS_FILTERS="test/deploy-tests-manifest.json" \
NEXT_TEST_VERSION="${{ github.event.inputs.nextVersion || needs.setup.outputs.next-version || 'canary' }}" \
VERCEL_CLI_VERSION="${{ github.event.inputs.vercelCliVersion || 'vercel@latest' }}" \
NEXT_TEST_DEPLOY_SCRIPT_PATH="${{ github.event.inputs.deployScriptPath || '' }}" \
NEXT_TEST_DEPLOY_LOGS_SCRIPT_PATH="${{ github.event.inputs.deployLogsScriptPath || '' }}" \
NEXT_TEST_CLEANUP_SCRIPT_PATH="${{ github.event.inputs.cleanupScriptPath || '' }}" \
node run-tests.js --timings --require-timings -g ${{ matrix.group }} -c 2 --type e2e
testTimingsArtifact: 'test-timings'
skipNativeBuild: 'yes'
skipNativeInstall: 'no'
stepName: 'test-deploy-webpack-${{ matrix.group }}'
timeout_minutes: 180
runs_on_labels: '["ubuntu-latest"]'
overrideProxyAddress: ${{ inputs.overrideProxyAddress || '' }}
test-deploy-turbopack:
name: Run Deploy Tests (Turbopack)
needs: [setup]
uses: ./.github/workflows/build_reusable.yml
secrets: inherit
strategy:
fail-fast: false
matrix:
group:
[
1/12,
2/12,
3/12,
4/12,
5/12,
6/12,
7/12,
8/12,
9/12,
10/12,
11/12,
12/12,
]
with:
afterBuild: |
npm i -g vercel@latest && \
NEXT_E2E_TEST_TIMEOUT=240000 \
NEXT_TEST_MODE=deploy \
IS_TURBOPACK_TEST=1 \
NEXT_TEST_CONTINUE_ON_ERROR="${{ github.event.inputs.continueOnError || false }}" \
NEXT_EXTERNAL_TESTS_FILTERS="test/deploy-tests-manifest.json" \
NEXT_TEST_VERSION="${{ github.event.inputs.nextVersion || needs.setup.outputs.next-version || 'canary' }}" \
VERCEL_CLI_VERSION="${{ github.event.inputs.vercelCliVersion || 'vercel@latest' }}" \
NEXT_TEST_DEPLOY_SCRIPT_PATH="${{ github.event.inputs.deployScriptPath || '' }}" \
NEXT_TEST_DEPLOY_LOGS_SCRIPT_PATH="${{ github.event.inputs.deployLogsScriptPath || '' }}" \
NEXT_TEST_CLEANUP_SCRIPT_PATH="${{ github.event.inputs.cleanupScriptPath || '' }}" \
node run-tests.js --timings --require-timings -g ${{ matrix.group }} -c 2 --type e2e
testTimingsArtifact: 'test-timings'
skipNativeBuild: 'yes'
skipNativeInstall: 'no'
stepName: 'test-deploy-turbopack-${{ matrix.group }}'
timeout_minutes: 180
runs_on_labels: '["ubuntu-latest"]'
overrideProxyAddress: ${{ inputs.overrideProxyAddress || '' }}
test-deploy-adapter:
name: Run Deploy Adapter Tests (Turbopack)
needs: setup
if: ${{ github.event.inputs.deployScriptPath == '' }}
uses: ./.github/workflows/build_reusable.yml
secrets: inherit
strategy:
fail-fast: false
matrix:
group:
[
1/12,
2/12,
3/12,
4/12,
5/12,
6/12,
7/12,
8/12,
9/12,
10/12,
11/12,
12/12,
]
with:
afterBuild: |
npm i -g vercel@latest && \
NEXT_E2E_TEST_TIMEOUT=240000 \
NEXT_TEST_MODE=deploy \
IS_TURBOPACK_TEST=1 \
NEXT_ENABLE_ADAPTER=1 \
NEXT_EXTERNAL_TESTS_FILTERS="test/deploy-tests-manifest.json" \
NEXT_TEST_VERSION="${{ github.event.inputs.nextVersion || needs.setup.outputs.next-version || 'canary' }}" \
VERCEL_CLI_VERSION="${{ github.event.inputs.vercelCliVersion || 'vercel@latest' }}" \
node run-tests.js --timings --require-timings -g ${{ matrix.group }} -c 2 --type e2e
testTimingsArtifact: 'test-timings'
skipNativeBuild: 'yes'
skipNativeInstall: 'no'
stepName: 'test-deploy-deploy-${{ matrix.group }}'
testReportsArtifactPrefix: 'adapter-test-reports'
timeout_minutes: 180
runs_on_labels: '["ubuntu-latest"]'
overrideProxyAddress: ${{ inputs.overrideProxyAddress || '' }}
report-test-results-to-datadog:
needs: [test-deploy-turbopack, test-deploy-webpack]
if: ${{ always() && github.event.inputs.deployScriptPath == '' }}
runs-on: ubuntu-latest
name: Report test results to datadog
steps:
- name: Download test report artifacts
id: download-test-reports
uses: actions/download-artifact@v4
with:
pattern: test-reports-*
path: test
merge-multiple: true
- name: Upload test report to datadog
run: |
if [ -d ./test/test-junit-report ]; then
DD_ENV=ci npx @datadog/datadog-ci@2.23.1 junit upload --tags test.type:deploy --service nextjs ./test/test-junit-report
fi
upload-adapter-test-results:
name: Upload adapter test results
needs: [test-deploy-adapter]
if: >-
${{
always() &&
github.event.inputs.deployScriptPath == '' &&
github.repository_owner == 'vercel' &&
(
(github.event_name == 'workflow_dispatch' && github.ref_name == 'canary') ||
(github.event_name == 'release' && github.event.release.target_commitish == 'canary')
)
}}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.9.0
check-latest: true
- name: Download adapter test result artifacts
continue-on-error: true
uses: actions/download-artifact@v4
with:
pattern: adapter-test-reports-*
path: adapter-test-results
merge-multiple: true
- name: Upload adapter test results
run: |
if [ -z "${ADAPTER_TEST_RESULTS_SECRET:-}" ]; then
echo "ADAPTER_TEST_RESULTS_SECRET is not configured, skipping upload"
exit 0
fi
if [ ! -d adapter-test-results ]; then
echo "No adapter test artifact directory found, skipping upload"
exit 0
fi
if ! find adapter-test-results -type f -name '*.results.json' | grep -q .; then
echo "No adapter .results.json files found, skipping upload"
exit 0
fi
node scripts/upload-adapter-test-results.mjs \
--results-root adapter-test-results \
--provider vercel \
--commit-sha "${{ github.sha }}"
env:
ADAPTER_TEST_RESULTS_SECRET: ${{ secrets.ADAPTER_TEST_RESULTS_SECRET }}
create-draft-prs:
name: Immediately open draft prs
needs: setup
if: ${{ github.repository_owner == 'vercel' && github.event_name == 'release' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- repo: front
workflow_id: cron-update-next.yml
- repo: v0
workflow_id: update-next.yaml
steps:
- name: Check token
run: gh auth status
# This sometimes fails for unknown reasons.
# Ignoring failures for now to check if a failure actually blocks subsequent steps
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GH_UPDATE_NEXT_WORKFLOW_TRIGGER }}
- uses: actions/github-script@v7
name: Check if target workflow is enabled
id: check-workflow-enabled
with:
retries: 3
retry-exempt-status-codes: 400,401,403,404,422
github-token: ${{ secrets.GH_UPDATE_NEXT_WORKFLOW_TRIGGER }}
result-encoding: string
script: |
const response = await github.request(
"GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}",
{
owner: "vercel",
repo: "${{ matrix.repo }}",
workflow_id: "${{ matrix.workflow_id }}",
}
);
const isEnabled = response.data.state === 'active';
console.info(`Target workflow state: ${response.data.state}`);
console.info(`Target workflow enabled: ${isEnabled}`);
return isEnabled ? 'true' : 'false';
- uses: actions/github-script@v7
name: Create draft PR for vercel/${{ matrix.repo }}
id: create-draft-pr
if: steps.check-workflow-enabled.outputs.result == 'true'
with:
retries: 3
retry-exempt-status-codes: 400,401,404
github-token: ${{ secrets.GH_UPDATE_NEXT_WORKFLOW_TRIGGER }}
result-encoding: string
script: |
const inputs = {
version: "${{ needs.setup.outputs.next-version }}"
};
await github.request(
"POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches",
{
owner: "vercel",
repo: "${{ matrix.repo }}",
workflow_id: "${{ matrix.workflow_id }}",
ref: "main",
inputs: inputs,
}
);
console.info(`Draft PR creation triggered for ${{ matrix.repo }}`);
console.info(`Workflow will create draft PR by default`);
update-prs:
name: Update prs as ready for review
needs: [test-deploy-turbopack, test-deploy-webpack, create-draft-prs]
if: ${{ (needs.test-deploy-webpack.result == 'success' || needs.test-deploy-webpack.result == 'skipped') && needs.test-deploy-turbopack.result == 'success' && github.repository_owner == 'vercel' && github.event_name == 'release' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- repo: front
workflow_id: cron-update-next.yml
workflow_url: https://github.com/vercel/front/actions/workflows/cron-update-next.yml?query=event%3Aworkflow_dispatch
- repo: v0
workflow_id: update-next.yaml
workflow_url: https://github.com/vercel/v0/actions/workflows/update-next.yaml?query=event%3Aworkflow_dispatch
steps:
- name: Check token
run: gh auth status
# This sometimes fails for unknown reasons.
# Ignoring failures for now to check if a failure actually blocks subsequent steps
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GH_UPDATE_NEXT_WORKFLOW_TRIGGER }}
- uses: actions/github-script@v7
name: Check if target workflow is enabled
id: check-workflow-enabled
with:
retries: 3
retry-exempt-status-codes: 400,401,404
github-token: ${{ secrets.GH_UPDATE_NEXT_WORKFLOW_TRIGGER }}
result-encoding: string
script: |
try {
const response = await github.request(
"GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}",
{
owner: "vercel",
repo: "${{ matrix.repo }}",
workflow_id: "${{ matrix.workflow_id }}",
}
);
const isEnabled = response.data.state === 'active';
console.info(`Target workflow state: ${response.data.state}`);
console.info(`Target workflow enabled: ${isEnabled}`);
return isEnabled ? 'true' : 'false';
} catch (error) {
console.error('Error checking workflow status:', error);
return 'false';
}
- uses: actions/github-script@v7
name: Mark PR ready for review in vercel/${{ matrix.repo }}
id: mark-pr-ready
if: steps.check-workflow-enabled.outputs.result == 'true'
with:
retries: 3
retry-exempt-status-codes: 400,401,404
# Default github token cannot dispatch events to the remote repo, it should be
# a PAT with Actions write access (https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event)
github-token: ${{ secrets.GH_UPDATE_NEXT_WORKFLOW_TRIGGER }}
# Note `workflow_id` and `inputs` are contract between vercel/${{ matrix.repo }},
# if these need to be changed both side should be updated accordingly.
script: |
const inputs = {
version: "${{ needs.setup.outputs.next-version }}",
'make-ready-for-review': 'true',
force: 'true'
};
await github.request(
"POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches",
{
owner: "vercel",
repo: "${{ matrix.repo }}",
workflow_id: "${{ matrix.workflow_id }}",
ref: "main",
inputs: inputs,
}
);
console.info("Tests passed - PR will be marked ready for review");
console.info(
"PR ready-for-review triggered in ${{ matrix.workflow_url }}"
);
test_e2e_project_reset_cron .github/workflows/test_e2e_project_reset_cron.yml
View raw YAML
name: test-e2e-project-reset-cron
on:
# Run every Sunday at 5AM UTC
schedule:
- cron: '0 5 * * 0'
# Allow manual triggering for emergency resets
workflow_dispatch:
env:
VERCEL_TEST_TEAM: vtest314-next-e2e-tests
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
VERCEL_ADAPTER_TEST_TEAM: vtest314-next-adapter-e2e-tests
VERCEL_ADAPTER_TEST_TOKEN: ${{ secrets.VERCEL_ADAPTER_TEST_TOKEN }}
NODE_LTS_VERSION: 20
TURBO_TEAM: 'vercel'
TURBO_CACHE: 'remote:rw'
run-name: test-e2e-project-reset (scheduled)
jobs:
reset-test-project:
runs-on: ubuntu-latest
if: github.repository_owner == 'vercel'
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup pnpm
run: |
npm i -g corepack@0.31
corepack enable
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 25
- name: Install dependencies
run: pnpm install
- name: Reset test project
run: node scripts/run-e2e-test-project-reset.mjs
test_examples matrix .github/workflows/test_examples.yml
View raw YAML
# This file duplicates bunch of things from build_test_deploy
on:
workflow_dispatch:
inputs:
is_dispatched:
description: 'Leave this option enabled'
required: true
default: true
type: boolean
schedule:
- cron: '0 */4 * * *'
name: Test examples
jobs:
testExamples:
# Don't execute using cron on forks
if: (github.repository == 'vercel/next.js') || (inputs.is_dispatched == true)
name: Test Examples
runs-on: ubuntu-latest
timeout-minutes: 120
env:
NEXT_TELEMETRY_DISABLED: 1
strategy:
fail-fast: false
matrix:
node: [20, 22]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- run: pnpm install
- run: pnpm build
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.35.1-focal /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && corepack enable > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=start xvfb-run node run-tests.js --type examples >> /proc/1/fd/1"
name: Run test/examples
triage perms .github/workflows/triage.yml
View raw YAML
name: Triage issues
on:
issues:
types: [opened, labeled]
issue_comment:
types: [created]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
issues: write
jobs:
triage:
name: Nissuer
runs-on: ubuntu-latest
if: >-
${{
(github.event_name != 'issue_comment' ||
(github.event_name == 'issue_comment' && !contains(github.event.issue.labels.*.name, 'stale'))) &&
github.event.issue.type.name != 'Documentation'
}}
steps:
- uses: balazsorban44/nissuer@1.10.0
with:
label-area-prefix: ''
label-area-match: 'name'
label-area-section: 'Which area\(s\) are affected\? \(Select all that apply\)(.*)### Additional context'
label-comments: |
{
"good first issue": ".github/comments/good-first-issue.md",
"please add a complete reproduction": ".github/comments/invalid-reproduction.md",
"please simplify reproduction": ".github/comments/simplify-reproduction.md",
"please verify canary": ".github/comments/verify-canary.md",
"resolved": ".github/comments/resolved.md"
}
reproduction-comment: '.github/comments/invalid-link.md'
reproduction-hosts: 'github.com,bitbucket.org,gitlab.com,codesandbox.io,stackblitz.com'
reproduction-blocklist: 'github.com/vercel/next.js.*,github.com/\\w*/?$,github.com$'
reproduction-link-section: '### Link to the code that reproduces this issue(.*)### To Reproduce'
reproduction-invalid-label: 'invalid link'
reproduction-issue-labels: 'bug,'
comment-unhelpful-weight: 0.5
webhook-url: ${{ secrets.NISSUER_WEBHOOK_URL }}
webhook-secret: ${{ secrets.NISSUER_WEBHOOK_SECRET }}
trigger_release .github/workflows/trigger_release.yml
View raw YAML
on:
schedule:
# run every day at 23:15
- cron: '15 23 * * *'
workflow_dispatch:
inputs:
releaseType:
description: stable, canary, beta, or release candidate?
required: true
type: choice
options:
- canary
- stable
- release-candidate
- beta
semverType:
description: semver type?
type: choice
options:
- patch
- minor
- major
force:
description: create a new release even if there are no new commits
default: false
type: boolean
name: Trigger Release
env:
NAPI_CLI_VERSION: 2.18.4
TURBO_VERSION: 2.8.11
NODE_LTS_VERSION: 20
jobs:
start:
if: github.repository_owner == 'vercel'
runs-on: ubuntu-latest
env:
NEXT_TELEMETRY_DISABLED: 1
# we build a dev binary for use in CI so skip downloading
# canary next-swc binaries in the monorepo
NEXT_SKIP_NATIVE_POSTINSTALL: 1
environment: release-${{ github.event.inputs.releaseType || 'canary' }}
steps:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
check-latest: true
- name: Clone Next.js repository
run: git clone https://github.com/vercel/next.js.git --depth=25 --single-branch --branch ${GITHUB_REF_NAME:-canary} .
- name: Check token
run: gh auth status
# This sometimes fails for unknown reasons.
# Ignoring failures for now to check if a failure truly implies a failed publish.
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
- name: Get commit of the latest tag
run: echo "LATEST_TAG_COMMIT=$(git rev-list -n 1 $(git describe --tags --abbrev=0))" >> $GITHUB_ENV
- name: Get latest commit
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 }}
run: |
if [ "$LATEST_TAG_COMMIT" = "$LATEST_COMMIT" ]; then
echo "No new commits. Exiting..."
exit 1
fi
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
pnpm --version
- id: get-store-path
run: echo STORE_PATH=$(pnpm store path) >> $GITHUB_OUTPUT
- uses: actions/cache@v4
timeout-minutes: 5
id: cache-pnpm-store
with:
path: ${{ steps.get-store-path.outputs.STORE_PATH }}
key: pnpm-store-v2-${{ hashFiles('pnpm-lock.yaml') }}
# Do not use restore-keys since it leads to indefinite growth of the cache.
- run: pnpm install
- run: node ./scripts/start-release.js --release-type ${{ github.event.inputs.releaseType || 'canary' }} --semver-type ${{ github.event.inputs.semverType }}
env:
RELEASE_BOT_GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
turbopack-benchmark .github/workflows/turbopack-benchmark.yml
View raw YAML
name: Turbopack Benchmark
on:
workflow_dispatch:
push:
branches:
- canary
pull_request:
types: ['opened', 'reopened', 'synchronize', 'labeled']
paths:
- '**/crates/**'
- '**/Cargo.toml'
- '**/Cargo.lock'
concurrency:
# Limit concurrent runs to 1 per PR, but allow concurrent runs on canary branch
group: ${{ github.event_name == 'pull_request' && format('{0}-{1}', github.workflow, github.event.pull_request.number) || format('{0}-{1}-{2}', github.workflow, github.ref_name, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
CI: 1
CARGO_INCREMENTAL: 0
# For faster CI
RUST_LOG: 'off'
TURBO_TEAM: 'vercel'
TURBO_CACHE: 'remote:rw'
jobs:
benchmark-small-apps:
name: Benchmark Rust Crates (small apps)
runs-on: ['self-hosted', 'linux', 'x64', 'metal']
steps:
- uses: actions/checkout@v4
- name: Setup Rust toolchain
uses: ./.github/actions/setup-rust
- name: Install cargo-codspeed
uses: taiki-e/install-action@v2
with:
tool: cargo-codspeed@3.0.5
- name: Cache on ${{ github.ref_name }}
uses: ijjk/rust-cache@turbo-cache-v1.0.9
with:
save-if: 'true'
cache-provider: 'turbo'
shared-key: build-turbopack-benchmark-small-apps-${{ hashFiles('.cargo/config.toml') }}
- name: Install pnpm dependencies
working-directory: turbopack/benchmark-apps
run: |
npm i -g corepack@0.31
corepack enable
pnpm install --loglevel error
- name: Build app build benchmarks
env:
CODSPEED_RUNNER_MODE: instrumentation
run: cargo codspeed build -p turbopack-cli small_apps
- name: Run the benchmarks
uses: CodSpeedHQ/action@v4
with:
mode: instrumentation
run: cargo codspeed run -p turbopack-cli small_apps
token: ${{ secrets.CODSPEED_TOKEN }}
benchmark-analyzer:
name: Benchmark Rust Crates (analyzer)
runs-on: ['self-hosted', 'linux', 'x64', 'metal']
steps:
- uses: actions/checkout@v4
- name: Setup Rust toolchain
uses: ./.github/actions/setup-rust
- name: Install cargo-codspeed
uses: taiki-e/install-action@v2
with:
tool: cargo-codspeed@3.0.5
- name: Cache on ${{ github.ref_name }}
uses: ijjk/rust-cache@turbo-cache-v1.0.9
with:
save-if: 'true'
cache-provider: 'turbo'
shared-key: build-turbopack-benchmark-analyzer-${{ hashFiles('.cargo/config.toml') }}
- name: Install pnpm dependencies
working-directory: turbopack/benchmark-apps
run: |
npm i -g corepack@0.31
corepack enable
pnpm install --loglevel error
- name: Build app build benchmarks
env:
CODSPEED_RUNNER_MODE: instrumentation
run: cargo codspeed build -p turbopack-ecmascript references
- name: Run the benchmarks
uses: CodSpeedHQ/action@v4
with:
mode: instrumentation
run: cargo codspeed run -p turbopack-ecmascript references
token: ${{ secrets.CODSPEED_TOKEN }}
benchmark-large:
name: Benchmark Rust Crates (large)
if: ${{ github.event.label.name == 'benchmark' || github.event_name == 'workflow_dispatch' }}
runs-on: ['self-hosted', 'linux', 'x64', 'metal']
steps:
- uses: actions/checkout@v4
- name: Setup Rust toolchain
uses: ./.github/actions/setup-rust
- name: Install cargo-codspeed
uses: taiki-e/install-action@v2
with:
tool: cargo-codspeed@3.0.5
- name: Build the benchmark target(s)
env:
CODSPEED_RUNNER_MODE: instrumentation
run: cargo codspeed build -p turbopack -p turbopack-bench
- name: Run the benchmarks
uses: CodSpeedHQ/action@v4
with:
mode: instrumentation
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}
turbopack-nextjs-build-integration-tests .github/workflows/turbopack-nextjs-build-integration-tests.yml
View raw YAML
name: Turbopack Next.js production integration tests
on:
schedule:
- cron: '0 6 * * *'
workflow_dispatch: {}
jobs:
test-dev:
name: Next.js integration tests
uses: ./.github/workflows/integration_tests_reusable.yml
with:
name: turbopack-production
test_type: production
run_before_test: |
export IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 RUST_BACKTRACE=1
# Failing tests take longer (due to timeouts and retries). Since we have
# many failing tests, we need smaller groups and longer timeouts, in case
# a group gets stuck with a cluster of failing tests.
e2e_groups: 12
integration_groups: 12
e2e_timeout_minutes: 60
integration_timeout_minutes: 60
secrets: inherit
turbopack-nextjs-dev-integration-tests .github/workflows/turbopack-nextjs-dev-integration-tests.yml
View raw YAML
name: Turbopack Next.js development integration tests
on:
schedule:
- cron: '0 6 * * *'
workflow_dispatch: {}
jobs:
test-dev:
name: Next.js integration tests
uses: ./.github/workflows/integration_tests_reusable.yml
with:
name: turbopack-development
test_type: development
run_before_test: |
export IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 RUST_BACKTRACE=1
secrets: inherit
turbopack-update-tests-manifest .github/workflows/turbopack-update-tests-manifest.yml
View raw YAML
# A recurring workflow which updates the passing/failing/skipped integration tests for Turbopack.
name: Update Turbopack test manifest
on:
# schedule:
# Every day at 7AM UTC https://crontab.guru/#0_7_*_*_*
# - cron: '0 7 * * *'
workflow_dispatch:
jobs:
update_dev_manifest:
name: Update and upload Turbopack development test manifest
if: github.repository_owner == 'vercel'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Commits made with the default `GITHUB_TOKEN` won't trigger workflows.
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow
token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: Install dependencies
shell: bash
run: pnpm i
- name: Create Pull Request
shell: bash
run: node scripts/automated-update-workflow.js
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
BRANCH_NAME: turbopack-manifest
SCRIPT: test/update-bundler-manifest.js --bundler turbopack --test-suite dev
PR_TITLE: Update Turbopack development test manifest
PR_BODY: This auto-generated PR updates the development integration test manifest used when testing Turbopack.
update_build_manifest:
name: Update and upload Turbopack production test manifest
if: github.repository_owner == 'vercel'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Commits made with the default `GITHUB_TOKEN` won't trigger workflows.
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow
token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: Install dependencies
shell: bash
run: pnpm i
- name: Create Pull Request
shell: bash
run: node scripts/automated-update-workflow.js
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
BRANCH_NAME: turbopack-manifest
SCRIPT: test/update-bundler-manifest.js --bundler turbopack --test-suite build
PR_TITLE: Update Turbopack production test manifest
PR_BODY: This auto-generated PR updates the production integration test manifest used when testing Turbopack.
update_fonts_data .github/workflows/update_fonts_data.yml
View raw YAML
name: Update Font Data
on:
# Run every every day at midnight https://crontab.guru/#0_0_*_*_*/1
schedule:
- cron: '0 0 * * */1'
# Allow manual runs
workflow_dispatch:
env:
NODE_LTS_VERSION: 20
jobs:
create-pull-request:
runs-on: ubuntu-latest
if: github.repository_owner == 'vercel'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Commits made with the default `GITHUB_TOKEN` won't trigger workflows.
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow
token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: Install dependencies
shell: bash
run: pnpm i
- name: Create Pull Request
shell: bash
run: node scripts/automated-update-workflow.js
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
BRANCH_NAME: fonts-data
SCRIPT: scripts/update-google-fonts.js
PR_TITLE: Update font data
PR_BODY: This auto-generated PR updates font data with latest available
update_react .github/workflows/update_react.yml
View raw YAML
name: Update React
on:
schedule:
# At 40 minutes past 16:00 on Mon, Tue, Wed, Thu, and Fri
# i.e. 30min past React nightlies: https://github.com/facebook/react/blob/941e1b4a0a81ca3d5f2ac6ef35682e2f8e96dae1/.github/workflows/runtime_prereleases_nightly.yml#L6
# TODO: automatically trigger on React release
- cron: 40 16 * * 1,2,3,4,5
# Allow manual runs
workflow_dispatch:
inputs:
version:
description: 'The version to update to. Uses latest Canary if omitted.'
required: false
env:
NODE_LTS_VERSION: 20
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
jobs:
create-pull-request:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Commits made with the default `GITHUB_TOKEN` won't trigger workflows.
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow
token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
- name: Set Git author
run: |
git config user.name "nextjs-bot"
git config user.email "it+nextjs-bot@vercel.com"
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: Install dependencies
shell: bash
# Just need scripts/ but those dependencies are listed in the workspace root.
run: pnpm install --filter .
- name: Create Pull Request
shell: bash
run: pnpm sync-react --actor "${{ github.actor }}" --commit --create-pull --version "${{ inputs.version }}"
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}
upload-tests-manifest .github/workflows/upload-tests-manifest.yml
View raw YAML
# Workflow to upload next.js integration test results to KV for https://areweturboyet.com/
# This workflow assumes the `next-integration-test` workflow has been executed
# and test reports have been uploaded to the `test-results` artifact.
name: Upload bundler test manifests to areweturboyet.com
on:
schedule:
- cron: '0 8 * * *'
workflow_dispatch: {}
push:
branches:
- canary
paths:
- 'test/*-manifest.json'
jobs:
upload_test_results:
name: Upload test results
runs-on: ubuntu-latest
steps:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_LTS_VERSION }}
check-latest: true
- name: Checkout
uses: actions/checkout@v4
- name: Setup corepack
run: |
npm i -g corepack@0.31
corepack enable
- name: Install dependencies
shell: bash
run: pnpm i
- name: 'Upload results to "Are We Turbo Yet" KV'
env:
TURBOYET_KV_REST_API_URL: ${{ secrets.TURBOYET_KV_REST_API_URL }}
TURBOYET_KV_REST_API_TOKEN: ${{ secrets.TURBOYET_KV_REST_API_TOKEN }}
TURBOYET_TOKEN: ${{ secrets.TURBOYET_TOKEN }}
uses: ./.github/actions/upload-turboyet-data