# Workflow to build and test wheels
name: Wheel builder
permissions:
  contents: read

on:
  schedule:
    # Nightly build at 3:42 A.M.
    - cron: "42 3 */1 * *"
  push:
    branches:
      - main
      # Release branches
      - "[0-9]+.[0-9]+.X"
  pull_request:
    branches:
      - main
      - "[0-9]+.[0-9]+.X"
  # Manual run
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  # Check whether to build the wheels and the source tarball
  check_build_trigger:
    name: Check build trigger
    runs-on: ubuntu-latest
    if: github.repository == 'jax-sklearn/jax-sklearn'
    outputs:
      build: ${{ steps.check_build_trigger.outputs.build }}

    steps:
      - name: Checkout jax-sklearn
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}

      - id: check_build_trigger
        name: Check build trigger
        run: bash build_tools/github/check_build_trigger.sh

  # Build the wheels for Linux, Windows and macOS for Python 3.9 and newer
  build_wheels:
    name: Build wheel for cp${{ matrix.python }}-${{ matrix.platform_id }}-${{ matrix.manylinux_image }}
    runs-on: ${{ matrix.os }}

    # For conda-incubator/setup-miniconda to work
    defaults:
      run:
        shell: bash -el {0}
    needs: check_build_trigger
    if: needs.check_build_trigger.outputs.build

    strategy:
      # Ensure that a wheel builder finishes even if another fails
      fail-fast: false
      matrix:
        include:
          # Window 64 bit
          - os: windows-latest
            python: 310
            platform_id: win_amd64
          - os: windows-latest
            python: 311
            platform_id: win_amd64
          - os: windows-latest
            python: 312
            platform_id: win_amd64
          - os: windows-latest
            python: 313
            platform_id: win_amd64
          - os: windows-latest
            python: 313t
            platform_id: win_amd64
            cibw_enable: cpython-freethreading

          # Linux 64 bit manylinux2014
          - os: ubuntu-latest
            python: 310
            platform_id: manylinux_x86_64
            manylinux_image: manylinux2014
          - os: ubuntu-latest
            python: 311
            platform_id: manylinux_x86_64
            manylinux_image: manylinux2014
          - os: ubuntu-latest
            python: 312
            platform_id: manylinux_x86_64
            manylinux_image: manylinux2014
          - os: ubuntu-latest
            python: 313
            platform_id: manylinux_x86_64
            manylinux_image: manylinux2014
          - os: ubuntu-latest
            python: 313t
            platform_id: manylinux_x86_64
            manylinux_image: manylinux2014
            cibw_enable: cpython-freethreading

          # # Linux 64 bit manylinux2014
          - os: ubuntu-24.04-arm
            python: 310
            platform_id: manylinux_aarch64
            manylinux_image: manylinux2014
          - os: ubuntu-24.04-arm
            python: 311
            platform_id: manylinux_aarch64
            manylinux_image: manylinux2014
          - os: ubuntu-24.04-arm
            python: 312
            platform_id: manylinux_aarch64
            manylinux_image: manylinux2014
          - os: ubuntu-24.04-arm
            python: 313
            platform_id: manylinux_aarch64
            manylinux_image: manylinux2014
          - os: ubuntu-24.04-arm
            python: 313t
            platform_id: manylinux_aarch64
            manylinux_image: manylinux2014
            cibw_enable: cpython-freethreading

          # MacOS x86_64
          - os: macos-13
            python: 310
            platform_id: macosx_x86_64
          - os: macos-13
            python: 311
            platform_id: macosx_x86_64
          - os: macos-13
            python: 312
            platform_id: macosx_x86_64
          - os: macos-13
            python: 313
            platform_id: macosx_x86_64
          - os: macos-13
            python: 313t
            platform_id: macosx_x86_64
            cibw_enable: cpython-freethreading

          # MacOS arm64
          - os: macos-14
            python: 310
            platform_id: macosx_arm64
          - os: macos-14
            python: 311
            platform_id: macosx_arm64
          - os: macos-14
            python: 312
            platform_id: macosx_arm64
          - os: macos-14
            python: 313
            platform_id: macosx_arm64
          - os: macos-14
            python: 313t
            platform_id: macosx_arm64
            cibw_enable: cpython-freethreading

    steps:
      - name: Checkout jax-sklearn
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11" # update once build dependencies are available

      - uses: conda-incubator/setup-miniconda@v3
        if: ${{ startsWith(matrix.platform_id, 'macosx') }}

      - name: Build and test wheels
        env:
          CIBW_ENABLE: ${{ matrix.cibw_enable }}
          CIBW_ENVIRONMENT: XLEARN_SKIP_NETWORK_TESTS=1
          CIBW_BUILD: cp${{ matrix.python }}-${{ matrix.platform_id }}
          CIBW_ARCHS: all
          CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_image }}
          CIBW_MANYLINUX_I686_IMAGE: ${{ matrix.manylinux_image }}
          # Needed on Windows CI to compile with Visual Studio compiler
          # otherwise Meson detects a MINGW64 platform and use MINGW64
          # toolchain
          CIBW_CONFIG_SETTINGS_WINDOWS: "setup-args=--vsenv"
          CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: bash build_tools/github/repair_windows_wheels.sh {wheel} {dest_dir}
          CIBW_BEFORE_BUILD: bash {project}/build_tools/wheels/cibw_before_build.sh {project}
          CIBW_BEFORE_TEST_WINDOWS: bash build_tools/github/build_minimal_windows_image.sh ${{ matrix.python }}
          CIBW_ENVIRONMENT_PASS_LINUX: RUNNER_OS
          CIBW_TEST_REQUIRES: pytest pandas
          # On Windows, we use a custom Docker image and CIBW_TEST_REQUIRES_WINDOWS
          # does not make sense because it would install dependencies in the host
          # rather than inside the Docker image
          CIBW_TEST_REQUIRES_WINDOWS: ""
          CIBW_TEST_COMMAND: bash {project}/build_tools/wheels/test_wheels.sh {project}
          CIBW_TEST_COMMAND_WINDOWS: bash {project}/build_tools/github/test_windows_wheels.sh ${{ matrix.python }} {project}
          CIBW_BUILD_VERBOSITY: 1

        run: bash build_tools/wheels/build_wheels.sh

      - name: Store artifacts
        uses: actions/upload-artifact@v4
        with:
          name: cibw-wheels-cp${{ matrix.python }}-${{ matrix.platform_id }}
          path: wheelhouse/*.whl

  update-tracker:
    uses: ./.github/workflows/update_tracking_issue.yml
    if: ${{ always() }}
    needs: [build_wheels]
    with:
      job_status: ${{ needs.build_wheels.result }}
    secrets:
      BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}

  # Build the source distribution under Linux
  build_sdist:
    name: Source distribution
    runs-on: ubuntu-latest
    needs: check_build_trigger
    if: needs.check_build_trigger.outputs.build

    steps:
      - name: Checkout jax-sklearn
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Build source distribution
        run: bash build_tools/github/build_source.sh

      - name: Test source distribution
        run: bash build_tools/github/test_source.sh
        env:
          XLEARN_SKIP_NETWORK_TESTS: 1

      - name: Store artifacts
        uses: actions/upload-artifact@v4
        with:
          name: cibw-sdist
          path: dist/*.tar.gz

  # Upload the wheels and the source distribution
  upload_anaconda:
    name: Upload to Anaconda
    runs-on: ubuntu-latest
    environment: upload_anaconda
    needs: [build_wheels, build_sdist]
    # The artifacts cannot be uploaded on PRs
    if: github.event_name != 'pull_request'

    steps:
      - name: Checkout jax-sklearn
        uses: actions/checkout@v4

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          pattern: cibw-*
          path: dist
          merge-multiple: true

      - name: Setup Python
        uses: actions/setup-python@v5

      - name: Upload artifacts
        env:
          # Secret variables need to be mapped to environment variables explicitly
          SCIKIT_LEARN_NIGHTLY_UPLOAD_TOKEN: ${{ secrets.SCIKIT_LEARN_NIGHTLY_UPLOAD_TOKEN }}
          SCIKIT_LEARN_STAGING_UPLOAD_TOKEN: ${{ secrets.SCIKIT_LEARN_STAGING_UPLOAD_TOKEN }}
          ARTIFACTS_PATH: dist
        # Force a replacement if the remote file already exists
        run: bash build_tools/github/upload_anaconda.sh
