name: Test

on:
  push:
    branches:
      - main
    tags:
      - "v*"
  pull_request:
    branches:
      - main
  workflow_dispatch:


permissions:
  contents: read

jobs:
###############################################################################
  test_linux:

    runs-on: ubuntu-22.04
    strategy:
      fail-fast: true
      max-parallel: 3
      matrix:
        python-version: ['3.11', '3.12', '3.13']

    steps:

    - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

    - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip' # caching pip dependencies
        allow-prereleases: true

    - name: setup apt dependencies
      run: |
        sudo apt-get update
        sudo apt-get install xvfb qt6-base-dev libhdf5-serial-dev libnetcdf-dev build-essential
        sudo apt-get install '^libxcb.*-dev' libx11-xcb-dev libglu1-mesa-dev libxrender-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev

        python -m pip install --upgrade pip

    - name: Test with pytest
      env:
        MPLBACKEND: agg
      run: |
        python -m pip install --upgrade pip
        python -m pip install wheel build
        python -m build
        python -m pip install dist/*.whl
        pip install -r .requirements.txt

        # uses xvfb for GUI part of the test
        pushd tools
        xvfb-run pytest --pyargs refnx
        popd

        # check that refnx gui starts
        # python tools/app/check_app_starts.py refnx

        # run vendored ptemcee tests
        pytest --pyargs refnx._lib.ptemcee.tests

    - name: Make sdist
      if: ${{ matrix.python-version == '3.9' }}
      run: |
        git clean -xdf
        python setup.py sdist

    - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
      if: ${{ matrix.python-version == '3.9' }}
      with:
        name: refnx-wheel-linux-${{ matrix.python-version }}
        path: dist/


#  linux_wheels:
#
#    runs-on: ubuntu-latest
#    strategy:
#      max-parallel: 2
#      matrix:
#        PLAT: ["manylinux2014_x86_64"]
#
#    steps:
#    - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
#    - name: Make Linux Wheel
#      run: |
#        docker run --rm -e="PLAT=${{ matrix.PLAT }}" -v $(pwd):/io quay.io/pypa/${{ matrix.PLAT }} /bin/bash /io/tools/build_manylinux_wheels.sh
#
#    - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
#      with:
#        name: refnx-wheel
#        path: dist/


###############################################################################
  test_macos_intel:

    runs-on: macos-13
    strategy:
      fail-fast: true
      max-parallel: 1
      matrix:
        python-version: [ '3.12' ]

    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

      - run: mkdir -p dist

      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip' # caching pip dependencies
          allow-prereleases: true

      - name: Make wheel
        shell: bash
        run: |

          python -m pip install --upgrade pip
          python -m pip install wheel delocate build
          python -m build . -v

      - name: Install package and test with pytest
        shell: bash
        env:
          MPLBACKEND: agg
        run: |
          pip install -r .requirements.txt
          pushd dist
          python -m pip install --only-binary=refnx --no-index --find-links=. refnx
          pytest --pyargs refnx
          popd

      - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: refnx-wheel-macos-${{ matrix.python-version }}-intel
          path: dist/


  test_macos:

    runs-on: macos-14
    strategy:
      fail-fast: true
      max-parallel: 3
      matrix:
        python-version: ['3.11', '3.12', '3.13']

    steps:
    - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

    - run: mkdir -p dist

    - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip' # caching pip dependencies
        allow-prereleases: true

    - name: install compilers
      shell: bash
      run: |
        brew install llvm

    - name: Make wheel
      shell: bash
      env:
        ARCHFLAGS: "-arch arm64"
        _PYTHON_HOST_PLATFORM: macosx-11.0-arm64
      run: |
        sudo xcode-select -s /Applications/Xcode_15.3.app
        export PATH="$PATH:/opt/homebrew/opt/llvm/bin"
        export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"
        export LDFLAGS="-L/opt/homebrew/opt/llvm/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm/lib/c++ -L/opt/homebrew/opt/llvm/lib"

        python -m pip install --upgrade pip
        python -m pip install wheel delocate build
        python -m build . -v
        # so that libomp is distributed with wheel
        delocate-wheel -v dist/refnx*.whl

    - name: Install package and test with pytest
      shell: bash
      env:
        MPLBACKEND: agg
      run: |
        python -m pip install -r .requirements.txt
        pushd dist
        python -m pip install --only-binary=refnx --no-index --find-links=. refnx
        pytest --pyargs refnx
        popd

    - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
      with:
        name: refnx-wheel-macos-${{ matrix.python-version }}
        path: dist/


  test_macos_app:
    needs: test_macos
    runs-on: macos-14
    strategy:
      fail-fast: true

    steps:
    - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

    - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
      with:
        python-version: '3.12'
        allow-prereleases: true

    - run: mkdir -p dist

    - name: Download wheel
      uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
      with:
        pattern: refnx-wheel-macos-3.12
        merge-multiple: true
        path: dist

    - name: Make frozen GUI executable
      run: |
        # make app in virtualenv
        python -m venv app
        source app/bin/activate
        python -m pip install --upgrade --upgrade-strategy eager -r tools/app/requirements.txt
        python -m pip install scipy
        pushd dist
        ls
        python -m pip install --only-binary=refnx --no-index --find-links=. refnx
        popd
        python -m pip install pyinstaller psutil
        # compileall in an effort to speedup pyinstaller GUI start
        python -m compileall

        pushd tools/app
        pyinstaller motofit.spec

        # check to see that the app starts
        python check_app_starts.py dist/refnx.app/Contents/MacOS/refnx
        popd
        printenv

    - name: Sign app and create dmg
      if: github.repository == 'refnx/refnx' && (github.event.pull_request.merged == true || startsWith(github.ref, 'refs/tags'))
      env:
        MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE_ISA }}
        MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_ISA_PWD }}
      run: |
        pushd tools/app
        echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
        ls -al certificate.p12
        security create-keychain -p DloaAcYP build.keychain
        security default-keychain -s build.keychain
        security unlock-keychain -p DloaAcYP build.keychain
        security import certificate.p12 -k build.keychain -P $MACOS_CERTIFICATE_PWD -T /usr/bin/codesign
        security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k DloaAcYP build.keychain >/dev/null
        security find-identity -p codesigning

        codesign --verify --options=runtime --entitlements entitlements.plist --timestamp --deep --verbose=4 --force --sign "Developer ID Application: The International Scattering Alliance (8CX8K63BQM)" dist/refnx.app

        cp ../../refnx/reflect/_app/icons/Motofit.icns .
        sips -i Motofit.icns
        DeRez -only icns Motofit.icns > icns.rsrc
        hdiutil create dist/refnx.dmg -srcfolder dist/refnx.app -ov -format UDZO
        Rez -append icns.rsrc -o dist/refnx.dmg
        SetFile -a C dist/refnx.dmg
        codesign -s "Developer ID Application: The International Scattering Alliance (8CX8K63BQM)" dist/refnx.dmg
        mv dist/refnx.dmg ../../dist/
        popd

# xcrun notarytool submit --apple-id "$APPLEID" --password "$APP_PASSWORD" --team-id 8CX8K63BQM --wait refnx.dmg
    - name: Notarize DMG
      if: github.repository == 'refnx/refnx' && (github.event.pull_request.merged == true || startsWith(github.ref, 'refs/tags'))
      uses: lando/notarize-action@b5c3ef16cf2fbcf2af26dc58c90255ec242abeed # v2.0.2
      with:
        product-path: "dist/refnx.dmg"
        primary-bundle-id: "com.refnx.refnx"
        appstore-connect-username: ${{ secrets.NOTARIZATION_USERNAME }}
        appstore-connect-password: ${{ secrets.NOTARIZATION_PASSWORD }}
        appstore-connect-team-id: 8CX8K63BQM
        verbose: True

    - name: Staple Release Build
      if: github.repository == 'refnx/refnx' && (github.event.pull_request.merged == true || startsWith(github.ref, 'refs/tags'))
      uses: BoundfoxStudios/action-xcode-staple@1e2200b448c6ed4dd44b963ff17d3e340fc6b064 # v1
      with:
        product-path: "dist/refnx.dmg"

    - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
      with:
        name: refnx-app-macos-${{ matrix.python-version }}
        path: dist/*.dmg


###############################################################################
  test_win:

    runs-on: windows-latest
    strategy:
      fail-fast: true
      max-parallel: 3
      matrix:
        python-version: ['3.11', '3.12', '3.13']

    steps:
    - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

    - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip' # caching pip dependencies
        allow-prereleases: true

    - run: pip install -r .requirements.txt

    - run: mkdir -p dist

    - name: Make wheel
      run: |
        python -m pip install --upgrade pip
        python -m pip install wheel
        python -m pip wheel . --no-deps -w dist

    - name: Install package and test with pytest
      if: ${{ matrix.python-version == '3.12' }}
      env:
        PYOPENCL_CTX: 0
      run: |
        cd dist
        python -m pip install --only-binary=refnx --no-index --find-links=. refnx

        #        python -m pip install pytools mako cffi
        #        choco install opencl-intel-cpu-runtime
        #        python -m pip install --only-binary=pyopencl --find-links http://www.silx.org/pub/wheelhouse/ --trusted-host www.silx.org pyopencl

        pytest --pyargs refnx
        cd ..

    - name: Check refnx gui starts
      if: ${{ matrix.python-version == '3.12' }}
      run: |
        # check that refnx gui starts
        pip install psutil
        python tools/app/check_app_starts.py refnx

    - name: Make frozen GUI executable
      if: ${{ matrix.python-version == '3.12' }}
      run: |
        # make app in virtualenv
        pip uninstall -y h5py
        python -m venv app
        app\Scripts\activate.bat
        python -m pip install --upgrade --upgrade-strategy eager -r tools/app/requirements.txt
        cd dist
        python -m pip install --only-binary=refnx --no-index --find-links=. refnx
        cd ..

        # fix for multiprocessing on Py3.9 is not merged in PyInstaller
        # if you are on Py3.7 you can just pip install pyinstaller
        # pip install git+https://github.com/andyfaff/pyinstaller.git@gh4865

        cd tools\app
        pyinstaller motofit.spec
        move dist\motofit.exe ..\..\dist\
        cd ..\..

    - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
      with:
        name: refnx-exe-win-${{ matrix.python-version }}
        path: dist/


###############################################################################
  build_doc:

    runs-on: ubuntu-latest
    strategy:
      max-parallel: 1
      matrix:
        python-version: ["3.12"]

    steps:
    - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
      with:
        python-version: ${{ matrix.python-version }}

    - name: setup apt dependencies
      run: |
        sudo apt-get update
        sudo apt-get install pandoc

    - name: Build documentation
      run: |
        python -m pip install --upgrade pip
        python -m pip install .
        python -m pip install wheel
        cd doc
        python -m pip install -r requirements.txt
        make html


###############################################################################
  lint:
    runs-on: ubuntu-latest
    strategy:
      max-parallel: 1
      matrix:
        python-version: ["3.12"]

    steps:
    - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
      with:
        python-version: ${{ matrix.python-version }}

    - name: Lint with flake8 and black
      run: |
        python -m pip install ruff black
        # stop the build if there are Python syntax errors or undefined names
        # the ignores are taken care of by black
        ruff check refnx
        black --check refnx

    - name: clang-format
      run: |
        sudo apt update
        sudo apt install clang-format
        cd src
        clang-format --Werror -n *.cpp *.h *.c
        cd pnr
        clang-format --Werror -n *.cc *.h
