name: CI

on:
  workflow_dispatch:
  push:
    branches:
      - main
  merge_group:
  pull_request:
    paths-ignore:
      - ".vscode/**"
      - "**/*.md"
      - ".github/ISSUE_TEMPLATE/**"

permissions: {}

# Automatically cancel older in-progress jobs on the same branch
concurrency:
  group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }}
  cancel-in-progress: true
defaults:
  run:
    shell: bash

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
  FORCE_COLOR: true
  ASTRO_TELEMETRY_DISABLED: true
  # 7 GiB by default on GitHub, setting to 6 GiB
  # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
  NODE_OPTIONS: --max-old-space-size=6144

jobs:
  changes:
    runs-on: ubuntu-latest
    permissions:
      contents: read # Required by actions/checkout
      pull-requests: read # Required by dorny/paths-filter to read changed files
    outputs:
      language-tools: ${{ steps.filter.outputs.language-tools }}
      astro-types: ${{ steps.filter.outputs.astro-types }}
      ci: ${{ steps.filter.outputs.ci }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3.0.3
        id: filter
        with:
          # When updating filters here, make sure to also add or remove them from the outputs block above.
          filters: |
            language-tools:
              - 'packages/language-tools/**'
            astro-types:
              - 'packages/astro/types/**'
              - 'packages/astro/astro-jsx.d.ts'
            ci:
              - '.github/workflows/ci.yml'

  # Build primes out Turbo build cache and pnpm cache
  build:
    name: "Build: ${{ matrix.os }}"
    runs-on: ${{ matrix.os }}
    timeout-minutes: 5
    permissions:
      contents: read # Checkout only
    strategy:
      matrix:
        # windows-latest is still windows-2022 for some reason, even though the windows-2025 image is GA
        OS: [ubuntu-latest, windows-2025]
        NODE_VERSION: [22]
      fail-fast: true
    steps:
      # Disable crlf so all OS can share the same Turbo cache
      # https://github.com/actions/checkout/issues/135
      - name: Disable git crlf
        run: git config --global core.autocrlf false

      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup PNPM
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

      - name: Setup node@${{ matrix.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: ${{ matrix.NODE_VERSION }}
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      # Only build in ubuntu as windows can share the build cache.
      # Also only build in core repo as forks don't have access to the Turbo cache.
      - name: Build Packages
        if: ${{ matrix.os == 'ubuntu-latest' && github.repository_owner == 'withastro' }}
        run: pnpm run build
        
  biome-lint:
    runs-on: ubuntu-latest
    permissions:
      contents: read # Checkout only
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - name: Setup Biome
        uses: biomejs/setup-biome@4c91541eaada48f67d7dbd7833600ce162b68f51 # v2.7.1
      - name: Run Biome
        run: biome ci --formatter-enabled=false --enforce-assist=false --reporter=default --reporter=github
  
  lint:
    name: Lint
    runs-on: ubuntu-latest
    timeout-minutes: 5
    permissions:
      contents: read # Checkout only
    needs: 
      - biome-lint
    steps:
      - name: Disable git crlf
        run: git config --global core.autocrlf false

      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup PNPM
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

      - name: Setup Node
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: 24.15.0
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Build Packages
        # The cache doesn't contain prebuild files and causes knip to fail
        run: pnpm run build --force

      - name: Lint source code
        run: pnpm run lint:ci

      - name: Lint publish code
        run: pnpm run publint

      - name: Type-check files
        run: pnpm run typecheck

  test:
    name: 'Test (${{ matrix.TEST_SUITE.name }}): ${{ matrix.os }} (node@${{ matrix.NODE_VERSION }})'
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30
    permissions:
      contents: read # Checkout only
    needs: build
    strategy:
      matrix:
        OS: [ubuntu-latest, macos-14, windows-2025]
        NODE_VERSION: [22, 24]
        TEST_SUITE:
          - { name: astro, script: 'pnpm run test:astro' }
          - { name: integrations, script: 'pnpm run test:integrations' }
        # We only test Node 22 on macOS and Windows to reduce CI usage.
        # This exclude config tells GitHub to skip the items in the matrix that run on macOS or
        # Windows and use one of the older Node versions.
        exclude:
          - os: macos-14
            NODE_VERSION: 22
          - os: windows-2025
            NODE_VERSION: 22
      fail-fast: false
    env:
      NODE_VERSION: ${{ matrix.NODE_VERSION }}
    steps:
      - name: Disable git crlf
        run: git config --global core.autocrlf false

      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0

      - name: Setup PNPM
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

      - name: Setup node@${{ matrix.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: ${{ matrix.NODE_VERSION }}
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Build Packages
        run: pnpm run build

      - name: Test ${{ matrix.TEST_SUITE.name }}
        run: ${{ matrix.TEST_SUITE.script }}
        env:
          # By default, Turborepo groups the logs in CI environments to prevent
          # the logs from being interleaved. We don't have log interleaving
          # issues here because we already passed the `--concurrency=auto` flag
          # to `turbo`.
          #
          # We want to force Turborepo to print the logs ASAP since it will help us
          # debug tests that time out, where we can know which step is stuck.
          #
          # https://github.com/vercel/turborepo/blob/v2.7.6/docs/site/content/docs/reference/run.mdx?plain=1#L361-L378
          # https://github.com/vercel/turborepo/blob/v2.7.6/docs/site/content/docs/reference/options-overview.mdx?plain=1#L63
          TURBO_LOG_ORDER: stream

  test-language-tools:
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30
    permissions:
      contents: read # Checkout only
    needs:
      - changes
      - build
    if: needs.changes.outputs.language-tools == 'true' || needs.changes.outputs.astro-types == 'true' || needs.changes.outputs.ci == 'true'
    strategy:
      matrix:
        os: [ubuntu-latest]
        # VS Code 1.101 (minimum supported version) ships with Node 22.15.1
        node_version: [22.22.2, 24.15.0]
        include:
          - os: windows-latest
            node_version: 22.22.2
          - os: macos-latest
            node_version: 22.22.2
      fail-fast: false

    name: 'Test (language-tools): node-${{ matrix.node_version }}, ${{ matrix.os }}'
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup PNPM
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

      - name: Setup node@${{ matrix.node_version }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: ${{ matrix.node_version }}
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Build the packages
        run: pnpm build

      - name: Test (Linux)
        if: runner.os == 'Linux'
        # On Linux, VS Code tests require a GUI environment, so we need to run tests using Xvfb
        run: |
          pnpm run test:language-tools:no-vs
          cd packages/language-tools/vscode
          xvfb-run -a pnpm test

      - name: Test ts-plugin (Linux, flaky)
        if: runner.os == 'Linux'
        timeout-minutes: 10
        working-directory: packages/language-tools/ts-plugin
        # TODO: Figure out why the vscode tests are fine, but the ts-plugin ones are super flaky
        run: xvfb-run -a pnpm test || echo "::warning title=ts-plugin tests failed::Known flaky; not failing CI."

      - name: Test
        if: runner.os != 'Linux'
        run: pnpm test:language-tools

  e2e:
    name: "Test (E2E): ${{ matrix.os }} (node@${{ matrix.NODE_VERSION }})"
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30
    permissions:
      contents: read # Checkout only
    needs: build
    strategy:
      matrix:
        OS: [ubuntu-latest, windows-2025]
        NODE_VERSION: [22]
      fail-fast: false
    env:
      NODE_VERSION: ${{ matrix.NODE_VERSION }}
    steps:
      - name: Disable git crlf
        run: git config --global core.autocrlf false

      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup PNPM
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

      - name: Setup node@${{ matrix.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: ${{ matrix.NODE_VERSION }}
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Build Packages
        run: pnpm run build

      - name: Test
        run: pnpm run test:e2e

  smoke:
    name: "Test (Smoke): ${{ matrix.os }} (node@${{ matrix.NODE_VERSION }})"
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30
    permissions:
      contents: read # Checkout only
    needs: build
    strategy:
      matrix:
        OS: [ubuntu-latest, windows-2025]
        NODE_VERSION: [22]
    env:
      NODE_VERSION: ${{ matrix.NODE_VERSION }}
    steps:
      - name: Disable git crlf
        run: git config --global core.autocrlf false

      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup PNPM
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

      - name: Setup node@${{ matrix.NODE_VERSION }}
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: ${{ matrix.NODE_VERSION }}
          cache: "pnpm"

      - name: Checkout docs
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          repository: withastro/docs
          path: smoke/docs
          # For a commit event on the `next` branch (`ref_name`), use the `v7` branch.
          # For a pull_request event merging into the `next` branch (`base_ref`), use the `v7` branch.
          # NOTE: For a pull_request event, the `ref_name` is something like `<pr-number>/merge` than the branch name.
          # NOTE: Perhaps docs repo should use a consistent `next` branch in the future.
          ref: ${{ (github.ref_name == 'next' || github.base_ref == 'next') && 'v7' || 'main' }}

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Build Packages
        run: pnpm run build

      - name: Remove docs translations except for English and Korean
        run: find smoke/docs/src/content/docs ! -name 'en' ! -name 'ko' -type d -mindepth 1 -maxdepth 1 -exec rm -rf {} +

      - name: Check if docs changed
        id: changes
        uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3.0.3
        with:
          filters: |
            docs:
              - 'packages/integrations/*/README.md'
              - "packages/astro/src/types/public/**"
              - 'packages/astro/src/core/errors/errors-data.ts'

      - name: Build autogenerated docs pages from current astro branch
        if: ${{ steps.changes.outputs.docs == 'true' }}
        run: cd smoke/docs && pnpm docgen && pnpm docgen:errors
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SOURCE_REPO: ${{ github.event.pull_request.head.repo.full_name || github.event.repository.full_name }}
          SOURCE_BRANCH: ${{ github.head_ref || github.ref_name }}

      - name: Test
        run: pnpm run test:smoke
        env:
          SKIP_OG: true
          PUBLIC_TWO_LANG: true
