next.js/.github/workflows/build_and_test.yml
build_and_test.yml1372 lines45.8 KB
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')
          )
        }}

  pr-ci-metadata:
    name: Upload PR CI metadata
    if: ${{ github.event_name == 'pull_request' }}
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Write PR metadata
        env:
          PR_NUMBER: ${{ github.event.pull_request.number }}
          PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
          PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
          PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
          PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
          PR_IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
        run: |
          mkdir -p pr-ci-metadata
          node <<'NODE'
          const fs = require('fs')

          fs.writeFileSync(
            'pr-ci-metadata/pr.json',
            JSON.stringify(
              {
                number: Number(process.env.PR_NUMBER),
                headSha: process.env.PR_HEAD_SHA,
                headRef: process.env.PR_HEAD_REF,
                headRepo: process.env.PR_HEAD_REPO,
                baseRef: process.env.PR_BASE_REF,
                isFork: process.env.PR_IS_FORK === 'true',
              },
              null,
              2
            )
          )
          NODE

      - name: Upload PR metadata
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: pr-ci-metadata
          path: pr-ci-metadata/pr.json
          retention-days: 1

  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'
      uploadNativeArtifact: 'next-swc-linux'
    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-latest-8-core-oss"]'
      buildNativeTarget: 'x86_64-pc-windows-msvc'
      uploadNativeArtifact: 'next-swc-windows'

    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@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

  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'
      skipInstallBuild: 'yes'
      afterBuild: pnpm dlx turbo@${TURBO_VERSION} run test-cargo-unit ${TURBO_ARGS}
      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 ${TURBO_ARGS}
      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@cf62e780f0c88301228978d593a7784427a097a6 # 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 ${TURBO_ARGS} -- --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 ${TURBO_ARGS}
      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-windows', 'build-next']
    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-latest-8-core-oss"]'
      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 }} \
          --preview-builds-base-url "${{ vars.PREVIEW_BUILDS_BASE_URL }}"
      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 }} \
          --preview-builds-base-url "${{ vars.PREVIEW_BUILDS_BASE_URL }}"
      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 }} \
          --preview-builds-base-url "${{ vars.PREVIEW_BUILDS_BASE_URL }}"
      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:
      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 }} \
          --preview-builds-base-url "${{ vars.PREVIEW_BUILDS_BASE_URL }}"
      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-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-latest-8-core-oss"]'
      buildNativeTarget: 'x86_64-pc-windows-msvc'
    secrets: inherit

  test-integration-windows:
    name: test integration windows
    needs: ['optimize-ci', 'changes', 'build-native-windows', '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-latest-8-core-oss"]'
      buildNativeTarget: 'x86_64-pc-windows-msvc'
    secrets: inherit

  test-prod-windows:
    name: test prod windows
    needs: ['optimize-ci', 'changes', 'build-native-windows', '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-latest-8-core-oss"]'
      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_TURBOPACK_TEST=1
        TURBOPACK_BUILD=1 NEXT_TEST_MODE=start 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

        TURBOPACK_DEV=1 NEXT_TEST_MODE=dev BROWSER_NAME=firefox node run-tests.js \
          test/e2e/app-dir/scss/hmr-module/hmr-module.test.ts

        TURBOPACK_DEV=1 NEXT_TEST_MODE=dev BROWSER_NAME=safari node run-tests.js \
          test/e2e/app-dir/scss/hmr-module/hmr-module.test.ts

        TURBOPACK_BUILD=1 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

        TURBOPACK_BUILD=1 NEXT_TEST_MODE=start 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

  test-node-streams-cache-components-dev:
    name: test node streams 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_USE_NODE_STREAMS=true
        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-node-streams-cache-components-dev-${{ matrix.group }}'
    secrets: inherit

  test-node-streams-cache-components-prod:
    name: test node streams 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_USE_NODE_STREAMS=true
        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-node-streams-cache-components-prod-${{ matrix.group }}'
    secrets: inherit

  test-node-streams-dev:
    name: test node streams dev
    needs: ['optimize-ci', 'changes', 'build-native', 'build-next']
    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_USE_NODE_STREAMS=true
        export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json,test/use-node-streams-tests-manifest.json"
        export NEXT_TEST_MODE=dev
        export IS_TURBOPACK_TEST=1
        export TURBOPACK_DEV=1
        export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
        export RUST_BACKTRACE=1

        node run-tests.js \
          --timings \
          -g ${{ matrix.group }} \
          --type development
      stepName: 'test-node-streams-dev-${{ matrix.group }}'
    secrets: inherit

  test-node-streams-prod:
    name: test node streams prod
    needs: ['optimize-ci', 'changes', 'build-native', 'build-next']
    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_USE_NODE_STREAMS=true
        export NEXT_EXTERNAL_TESTS_FILTERS="test/cache-components-tests-manifest.json,test/use-node-streams-tests-manifest.json"
        export NEXT_TEST_MODE=start
        export IS_TURBOPACK_TEST=1
        export TURBOPACK_BUILD=1
        export __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true
        export RUST_BACKTRACE=1

        node run-tests.js \
          --timings \
          -g ${{ matrix.group }} \
          --type production
      stepName: 'test-node-streams-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-node-streams-cache-components-dev',
        'test-node-streams-cache-components-prod',
        'test-node-streams-dev',
        'test-node-streams-prod',
        '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')) }}
Quest for Codev2.0.0
/
SIGN IN