name: CI
on:
  push:
    branches:
      - "main"
  pull_request:
    branches:
      - "main"
  workflow_dispatch: # allows you to trigger manually

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  FORCE_COLOR: 3
  PIXI_VERSION: "v0.63.2"

jobs:
  detect-ci-trigger:
    name: detect ci trigger
    runs-on: ubuntu-latest
    if: |
      github.repository == 'pydata/xarray'
      && (github.event_name == 'push' || github.event_name == 'pull_request')
      && !contains(github.event.pull_request.labels.*.name, 'skip-ci')
    outputs:
      triggered: ${{ steps.detect-trigger.outputs.trigger-found }}
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 2
      - uses: xarray-contrib/ci-trigger@v1
        id: detect-trigger
        with:
          keyword: "[skip-ci]"

  cache-pixi-lock:
    uses: ./.github/workflows/cache-pixi-lock.yml
    with:
      pixi-version: "v0.63.2" # keep in sync with env var above
  test:
    name: "${{ matrix.os }} | ${{ matrix.pixi-env }}${{ matrix.pytest-addopts && format(' ({0})', matrix.pytest-addopts) || '' }}"
    runs-on: ${{ matrix.os }}
    needs: [detect-ci-trigger, cache-pixi-lock]
    if: needs.detect-ci-trigger.outputs.triggered == 'false'
    defaults:
      run:
        shell: bash -l {0}
    strategy:
      fail-fast: false
      matrix:
        os: ["ubuntu-latest", "macos-latest", "windows-latest"]
        # Bookend python versions
        pixi-env: ["test-py311", "test-py313"]
        pytest-addopts: [""]
        include:
          # Minimum python version:
          - pixi-env: "test-py311-bare-minimum"
            os: ubuntu-latest
          - pixi-env: "test-py311-bare-min-and-scipy"
            os: ubuntu-latest
          - pixi-env: "test-py311-min-versions"
            os: ubuntu-latest
          # Latest python version:
          - pixi-env: "test-py313-no-numba"
            os: ubuntu-latest
          - pixi-env: "test-py313-no-dask"
            os: ubuntu-latest
          - pixi-env: "test-py313"
            pytest-addopts: "flaky"
            os: ubuntu-latest
          # The mypy tests must be executed using only 1 process in order to guarantee
          # predictable mypy output messages for comparison to expectations.
          - pixi-env: "test-py311-with-typing"
            pytest-addopts: "mypy"
            numprocesses: 1
            os: ubuntu-latest
          - pixi-env: "test-py313-with-typing"
            numprocesses: 1
            os: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0 # Fetch all history for all branches and tags.
      - name: Restore cached pixi lockfile
        uses: actions/cache/restore@v5
        id: restore-pixi-lock
        with:
          enableCrossOsArchive: true
          path: |
            pixi.lock
          key: ${{ needs.cache-pixi-lock.outputs.cache-id }}
      - uses: prefix-dev/setup-pixi@v0.9.3
        with:
          pixi-version: ${{ env.PIXI_VERSION }}
          cache: true
          environments: ${{ matrix.pixi-env }}
          cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}

      - name: Set environment variables
        run: |
          echo "TODAY=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
          echo "PYTHON_VERSION=$(pixi run -e ${{ matrix.pixi-env }} -- python --version | cut -d' ' -f2 | cut -d. -f1,2)" >> $GITHUB_ENV

          if [[ "${{ matrix.pytest-addopts }}" != "" ]] ;
          then
            if [[ "${{ matrix.pytest-addopts }}" == "flaky" ]] ;
            then
              echo "PYTEST_ADDOPTS=-m 'flaky or network' --run-flaky --run-network-tests -W default" >> $GITHUB_ENV
            elif [[ "${{ matrix.pytest-addopts }}" == "mypy" ]] ;
            then
              echo "PYTEST_ADDOPTS=-n 1 -m 'mypy' --run-mypy -W default" >> $GITHUB_ENV
            fi
            if [[ "${{ matrix.pixi-env }}" == "min-all-deps" ]] ;
            then
              # Don't raise on warnings
              echo "PYTEST_ADDOPTS=-W default" >> $GITHUB_ENV
            fi
          fi

      # We only want to install this on one run, because otherwise we'll have
      # duplicate annotations.
      - name: Install error reporter
        if: ${{ matrix.os }} == 'ubuntu-latest' and ${{ matrix.pixi-env}} == 'test'
        run: |
          pixi add --pypi pytest-github-actions-annotate-failures

      - name: Version info
        run: |
          pixi run -e ${{ matrix.pixi-env }} -- python xarray/util/print_versions.py

      - name: Import xarray
        run: |
          pixi run -e ${{ matrix.pixi-env }} -- python -c "import xarray"

      - name: Restore cached hypothesis directory
        uses: actions/cache@v5
        with:
          path: .hypothesis/
          key: cache-hypothesis
          enableCrossOsArchive: true
          save-always: true

      - name: Run tests
        run: |
          pixi run -e ${{ matrix.pixi-env }} -- python -m pytest -n ${{ matrix.numprocesses || 4 }} \
            --timeout 180 \
            --cov=xarray \
            --cov-report=xml \
            --junitxml=pytest.xml

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v6
        with:
          name: Test results for OS ${{ runner.os }} pixi-env ${{ matrix.pixi-env }} pytest-addopts ${{ matrix.pytest-addopts }}
          path: pytest.xml

      - name: Upload code coverage to Codecov
        uses: codecov/codecov-action@v5.5.2
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
        with:
          file: ./coverage.xml
          flags: unittests
          env_vars: RUNNER_OS,PYTHON_VERSION
          name: codecov-umbrella
          fail_ci_if_error: false

  event_file:
    name: "Event File"
    runs-on: ubuntu-latest
    if: github.repository == 'pydata/xarray'
    steps:
      - name: Upload
        uses: actions/upload-artifact@v6
        with:
          name: Event File
          path: ${{ github.event_path }}
