Files
vercel__next.js/.github/workflows/test_e2e_deploy_release.yml
Sebastian "Sebbie" Silbermann 988a6ab727 [ci] Authenticate Turborepo remote caching with OIDC instead of a static PAT (#97590)
CI authenticated to Vercel Remote Cache with a long-lived Personal
Access Token in the `TURBO_TOKEN` repository secret. That token never
expired, is scoped to a team member rather than the team, and is
readable by every job that inherits secrets.

Each job now mints its own short-lived, cache-only token instead, using
`vercel/setup-turborepo-remote-cache-action` against a Turborepo CLI
OIDC policy configured on the Vercel team following
https://vercel.com/docs/monorepos/remote-caching/external-ci-cd#openid-connect-oidc

Forks skip the step entirely since they won't have access to repository
variables. During outages or any other permission errors, the steps
outcome will simply be ignore and we fall back to uncached behavior.

This could lead to silent regressions or hiding new, incorrect callsites
lacking necessary permissions. A Datadog monitor is not as simple as I'd
like since DD does not track outcome but conclusion (which is always
success for continue-on-error). Adding custom tags via DD CLI feels to
heavy. We'll revisit if this becomes a recurring issue.
2026-08-20 10:16:10 +02:00

399 lines
14 KiB
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: >-
Version of `next` to test against. Anything installable by npm:
a dist-tag (canary), an exact version (16.2.4), or a custom https
tarball URL. Leave empty to use the `next` version from
packages/next/package.json.
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
forceBuildInHive:
description: Hive ID to force the build onto
default: ''
type: string
buildContainerVersion:
description: >-
Build container image tag. Requires forceBuildInHive.
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 cannot read job outputs, so for release events it shows the tag name,
# assuming the release tag matches the `next` version checked into packages/next/package.json
run-name: test-e2e-deploy ${{ inputs.nextVersion || (github.event_name == 'release' && github.event.release.tag_name) || 'repo version' }}
jobs:
setup:
runs-on: ubuntu-latest
if: github.repository_owner == 'vercel'
outputs:
next-version: ${{ steps.version.outputs.value }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 25
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: .node-version
check-latest: true
package-manager-cache: false
- name: Setup pnpm
run: |
npm i -g corepack@0.31
corepack enable
- id: nextPackageInfo
name: Get `next` package info
run: |
cd packages/next
{
echo 'value<<EOF'
cat package.json
echo EOF
} >> "$GITHUB_OUTPUT"
- id: version
name: Resolve `next` version
env:
INPUT_NEXT_VERSION: ${{ inputs.nextVersion }}
run: |
set -euo pipefail
if [ -n "$INPUT_NEXT_VERSION" ]; then
echo "value=$INPUT_NEXT_VERSION" >> "$GITHUB_OUTPUT"
else
echo 'value=${{ fromJson(steps.nextPackageInfo.outputs.value).version }}' >> "$GITHUB_OUTPUT"
fi
- name: Log resolved `next` version
env:
RESOLVED_NEXT_VERSION: ${{ steps.version.outputs.value }}
run: |
echo "Resolved next version: $RESOLVED_NEXT_VERSION"
- 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-timings
path: test-timings.json
# Allows "rerun failed jobs" for N days
retention-days: 5
if-no-files-found: error
# Runs last in setup so the steps above overlap with npm propagation.
- name: wait-for-published-next
env:
# The spec that comes after `next@` when the deploy tests install
# it: a dist-tag, an exact version, or a tarball URL.
NEXT_VERSION_SPEC: ${{ steps.version.outputs.value }}
POLL_TIMEOUT_SECONDS: 900
POLL_INTERVAL_SECONDS: 15
run: |
set -euo pipefail
if [[ "$NEXT_VERSION_SPEC" == http* ]]; then
echo "next@$NEXT_VERSION_SPEC is a tarball URL, skipping npm availability check"
exit 0
fi
deadline=$((SECONDS + POLL_TIMEOUT_SECONDS))
attempt=1
while true; do
if resolved="$(pnpm view "next@$NEXT_VERSION_SPEC" version)"; then
echo "next@$NEXT_VERSION_SPEC is available on npm (resolved to $resolved, attempt $attempt)"
break
fi
if (( SECONDS >= deadline )); then
echo "::error::Timed out after ${POLL_TIMEOUT_SECONDS}s waiting for next@$NEXT_VERSION_SPEC to become available on npm"
exit 1
fi
echo "next@$NEXT_VERSION_SPEC is not available on npm yet (attempt $attempt), retrying in ${POLL_INTERVAL_SECONDS}s"
sleep "$POLL_INTERVAL_SECONDS"
attempt=$((attempt + 1))
done
test-deploy-webpack:
name: Run Deploy Tests (Webpack)
needs: setup
if: ${{ github.event.inputs.deployScriptPath == '' }}
permissions:
contents: read
id-token: write
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_ENABLE_ADAPTER=0 \
NEXT_EXTERNAL_TESTS_FILTERS="test/deploy-tests-manifest.json" \
NEXT_TEST_VERSION="${{ needs.setup.outputs.next-version }}" \
VERCEL_CLI_VERSION="${{ github.event.inputs.vercelCliVersion || 'vercel@latest' }}" \
VERCEL_FORCE_BUILD_IN_HIVE="${{ github.event.inputs.forceBuildInHive || '' }}" \
VERCEL_BUILD_CONTAINER_VERSION="${{ github.event.inputs.buildContainerVersion || '' }}" \
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 || '' }}" \
NEXT_TEST_PREVIEW_BUILDS_BASE_URL="${{ vars.PREVIEW_BUILDS_BASE_URL }}" \
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]
permissions:
contents: read
id-token: write
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=0 \
NEXT_TEST_CONTINUE_ON_ERROR="${{ github.event.inputs.continueOnError || false }}" \
NEXT_EXTERNAL_TESTS_FILTERS="test/deploy-tests-manifest.json" \
NEXT_TEST_VERSION="${{ needs.setup.outputs.next-version }}" \
VERCEL_CLI_VERSION="${{ github.event.inputs.vercelCliVersion || 'vercel@latest' }}" \
VERCEL_FORCE_BUILD_IN_HIVE="${{ github.event.inputs.forceBuildInHive || '' }}" \
VERCEL_BUILD_CONTAINER_VERSION="${{ github.event.inputs.buildContainerVersion || '' }}" \
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 || '' }}" \
NEXT_TEST_PREVIEW_BUILDS_BASE_URL="${{ vars.PREVIEW_BUILDS_BASE_URL }}" \
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 == '' }}
permissions:
contents: read
id-token: write
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="${{ needs.setup.outputs.next-version }}" \
VERCEL_CLI_VERSION="${{ github.event.inputs.vercelCliVersion || 'vercel@latest' }}" \
VERCEL_FORCE_BUILD_IN_HIVE="${{ github.event.inputs.forceBuildInHive || '' }}" \
VERCEL_BUILD_CONTAINER_VERSION="${{ github.event.inputs.buildContainerVersion || '' }}" \
NEXT_TEST_PREVIEW_BUILDS_BASE_URL="${{ vars.PREVIEW_BUILDS_BASE_URL }}" \
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:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: |
.github
persist-credentials: false
- name: Download test report artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: test-reports-*
path: test
merge-multiple: true
- name: Install datadog-ci
if: ${{ env.DATADOG_API_KEY != '' }}
uses: ./.github/actions/setup-datadog-ci
- name: Upload test report to datadog
if: ${{ env.DATADOG_API_KEY != '' }}
run: |
if [ -d ./test/test-junit-report ]; then
DD_ENV=ci "$DATADOG_CI_PATH" junit upload --skip-git-metadata-upload --tags test.type:deploy --service nextjs ./test/test-junit-report
fi
upload-adapter-test-results:
name: Upload adapter test results
needs: [setup, test-deploy-adapter]
if: >-
${{
always() &&
github.event.inputs.deployScriptPath == '' &&
github.repository_owner == 'vercel' &&
!startsWith(needs.setup.outputs.next-version, 'http')
}}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 20.9.0
check-latest: true
package-manager-cache: false
- name: Download adapter test result artifacts
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
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 }}