name: build

on:
    pull_request:
    push:
        branches:
            - master
            - 'release/**'
        tags:
          - '*'

# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
# Only cancels-in-progress on PRs (head_ref only defined in PR, fallback run_id always unique)
concurrency:
  group: ${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:

  actions-linting:
    uses: cvxpy/actions/.github/workflows/linting.yml@main

  build:
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        shell: bash -l {0}
    strategy:
      fail-fast: false
      matrix:
        os: [ ubuntu-22.04, macos-13, windows-2022 ]
        python-version: [ 3.9, "3.10", "3.11", "3.12", "3.13" ]
        include:
          # These are intended to just add their extra parameter to existing matrix combinations;
          # see https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude
          - os: ubuntu-22.04
            python-version: 3.12
            openmp: "True"
            single_action_config: false
          - os: macos-13
            python-version: 3.12
            single_action_config: true

    env:
      RUNNER_OS: ${{ matrix.os }}
      PYTHON_VERSION: ${{ matrix.python-version }}
      SINGLE_ACTION_CONFIG: "${{ matrix.single_action_config && 'True' || 'False' }}"
      USE_OPENMP: "${{ matrix.openmp == 'True' && 'True' || 'False' }}"
      MOSEK_CI_BASE64: ${{ secrets.MOSEK_CI_BASE64 }}

    steps:
      - uses: actions/checkout@v4
      - name: Set Additional Envs
        run: |
          echo "PYTHON_SUBVERSION=$(echo $PYTHON_VERSION | cut -c 3-)" >> $GITHUB_ENV
          echo $MOSEK_CI_BASE64 | base64 -d > mosek.lic
          echo "MOSEKLM_LICENSE_FILE=$( [[ $RUNNER_OS == 'macOS' ]] && echo $(pwd)/mosek.lic || echo $(realpath mosek.lic) )" >> $GITHUB_ENV
      - uses: conda-incubator/setup-miniconda@v3
        with:
          auto-update-conda: true
          python-version: ${{ matrix.python-version }}
          channels: conda-forge,anaconda

      - name: Install
        run: |
          source continuous_integration/install_dependencies.sh

      - name: Test
        run: |
          source continuous_integration/test_script.sh

      - name: Upload coverage file
        uses: actions/upload-artifact@v4
        if: ${{env.SINGLE_ACTION_CONFIG == 'True'}}
        with:
          name: coverage
          path: coverage.xml

  build_wheels:
    needs: build

    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ ubuntu-22.04, macos-13, windows-2022 ]
        python-version: [ 3.9, "3.10", "3.11", "3.12", "3.13" ]
        build-cvxpy-base: [ true, false ]  # whether to build cvxpy-base (true) or regular cvxpy (false)
        include:
          # This is intended to just add the single_action_config parameter to one existing combination;
          # see https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude
          - os: ubuntu-22.04
            python-version: 3.12
            single_action_config: true

    env:
      RUNNER_OS: ${{ matrix.os }}
      PYTHON_VERSION: ${{ matrix.python-version }}
      SINGLE_ACTION_CONFIG: "${{ matrix.single_action_config }}"
      PIP_INSTALL: "${{ matrix.python-version == '3.11' }}"
      PYPI_SERVER: ${{ secrets.PYPI_SERVER }}
      PYPI_USER: ${{ secrets.PYPI_USER }}
      PYPI_PASSWORD: ${{ matrix.build-cvxpy-base && secrets.PYPI_BASE_PASSWORD || secrets.PYPI_PASSWORD }}
      CVXPY_BASE_SUFFIX: ${{ matrix.build-cvxpy-base && 'base-' || '' }}

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Set Additional Envs
        shell: bash
        run: |
          echo "PYTHON_SUBVERSION=$(echo $PYTHON_VERSION | cut -c 3-)" >> $GITHUB_ENV
          echo "DEPLOY=$( [[ $GITHUB_EVENT_NAME == 'push' && $GITHUB_REF == 'refs/tags'* ]] && echo 'True' || echo 'False' )" >> $GITHUB_ENV

      - name: Adapt pyproject.toml
        if: ${{matrix.build-cvxpy-base}}
        shell: bash
        run: |
          # Mac has a different syntax for sed -i, this works across oses
          sed -i.bak -e 's/name = "cvxpy"/name = "cvxpy-base"/g' pyproject.toml
          sed -i.bak '/clarabel >= /d' pyproject.toml
          sed -i.bak '/osqp >= /d' pyproject.toml
          sed -i.bak '/ecos >= /d' pyproject.toml
          sed -i.bak '/scs >= /d' pyproject.toml
          sed -i.bak -e 's/name="cvxpy",/name="cvxpy-base",/g' setup.py
          rm -rf pyproject.toml.bak setup.py.bak

      - name: Verify that we can build by pip-install on each OS.
        if: ${{matrix.build-cvxpy-base && env.PIP_INSTALL == 'True'}}
        run: |
          pip install . pytest cplex
          pytest cvxpy/tests/test_conic_solvers.py -k 'TestCPLEX'
          # ensure a fresh state for cibuildwheel
          rm -r build

      - name: Set up QEMU  # For aarch64, see https://cibuildwheel.readthedocs.io/en/stable/faq/#emulation
        if: runner.os == 'Linux'
        uses: docker/setup-qemu-action@v3
        with:
          platforms: all

      - name: Build wheels
        if: ${{github.event_name == 'push' && env.USE_OPENMP != 'True'}}
        env:
          CIBW_BUILD: "cp3${{env.PYTHON_SUBVERSION}}-*"
          CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux*"
          CIBW_ARCHS_MACOS: x86_64 universal2
          CIBW_ARCHS_LINUX: auto aarch64
        uses: pypa/cibuildwheel@v2.21.3

      - name: Build source
        if: ${{github.event_name == 'push' && env.SINGLE_ACTION_CONFIG == 'True'}}
        run: |
          pip install build
          python -m build --sdist -o wheelhouse

      - name: Check wheels
        if: ${{github.event_name == 'push' && env.USE_OPENMP != 'True'}}
        shell: bash
        run: |
          python -m pip install --upgrade twine
          twine check wheelhouse/*

      - name: Release to pypi
        if: ${{env.DEPLOY == 'True' && env.USE_OPENMP != 'True'}}
        shell: bash
        run: |
          twine upload --skip-existing --repository-url $PYPI_SERVER wheelhouse/* -u $PYPI_USER -p $PYPI_PASSWORD

      - name: Upload artifacts to github
        if: ${{github.event_name == 'push' && env.USE_OPENMP != 'True'}}
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ env.CVXPY_BASE_SUFFIX }}${{ matrix.os }}-${{ matrix.python-version }}
          path: ./wheelhouse
