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<> $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 retention-days: 1 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 mold: 'yes' 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: afterBuild: | 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_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')) }}