name: AWS GPU

on:
  push:
    branches: [main, "[0-9]+.[0-9]+.x"]
  pull_request:
    types:
      - labeled
      - opened
      - synchronize

env:
  PYTEST_ADDOPTS: "-v --color=yes"
  FORCE_COLOR: "1"

# Cancel the job if new commits are pushed
# https://stackoverflow.com/questions/66335225/how-to-cancel-previous-runs-in-the-pr-when-you-push-new-commitsupdate-the-curre
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

# There are two jobs:
# 1. `check` determines if the second job (`test`) will be run (through a job dependency).
# 2. `test` runs on an AWS runner and executes the GPU tests.
jobs:
  # If the `skip-gpu-ci` label is set, this job is skipped, and consequently the `test` job too.
  # If the `run-gpu-ci` label is set or we reacted to a `push` event, this job succeeds (and `test` is run).
  # If neither is set, this job fails, `test` is skipped, and the whole workflow fails.
  check:
    name: "Triage: Check if GPU tests are allowed to run"
    if: (!contains(github.event.pull_request.labels.*.name, 'skip-gpu-ci'))
    runs-on: ubuntu-latest
    steps:
      - uses: flying-sheep/check@v1
        with:
          success: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'run-gpu-ci') }}
  # If `check` wasn’t skipped or failed, start an AWS runner and run the GPU tests on it.
  test:
    name: GPU Tests
    needs: check
    runs-on: "cirun-aws-gpu--${{ github.run_id }}"
    # Setting a timeout of 30 minutes, as the AWS costs money
    # At time of writing, a typical run takes about 5 minutes
    timeout-minutes: 30

    steps:
      - uses: actions/checkout@v4 # TODO: upgrade once cirun image supports node 24
        with:
          fetch-depth: 0
          filter: blob:none

      - name: Nvidia SMI sanity check
        run: nvidia-smi

      - name: Install yq
        run: |
          sudo snap install yq

      - name: Extract max Python version from classifiers
        run: |
          classifiers=$(yq .project.classifiers pyproject.toml -oy  | grep --only-matching --perl-regexp '(?<=Python :: )(\d\.\d+)')
          max_version=$(echo "$classifiers" | sort -V | tail -1)
          echo "max_python_version=$max_version" >> $GITHUB_ENV

      - name: Install UV
        uses: astral-sh/setup-uv@v6 # TODO: upgrade once cirun image supports node 24
        with:
          enable-cache: true
          # Any Cuda 14+ will support Python 3.14: https://github.com/cupy/cupy/issues/9346
          python-version: '3.13'  # ${{ env.max_python_version }}

      - name: Install AnnData
        run: |
          uv venv
          uv pip install -e ".[dev,test,cu12]" -c ci/constraints.txt

      - name: Env list
        run: uv pip list

      - name: Run test
        env:
          COVERAGE_PROCESS_START: ${{ github.workspace }}/pyproject.toml
        run: |
          uv run coverage run -m pytest -m gpu -n auto
          uv run coverage combine
          uv run coverage xml

      - uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          fail_ci_if_error: true
          files: test-data/coverage.xml

      - name: Remove 'run-gpu-ci' Label
        if: always()
        uses: actions-ecosystem/action-remove-labels@v1
        with:
          labels: "run-gpu-ci"
          github_token: ${{ secrets.GITHUB_TOKEN }}
