name: Build plugin

on:
  push:
    branches:
      - main
    tags:
      - 'v*'
  pull_request:
    branches:
      - main

env:
  PLUGIN_NAME: QodeAssist
  MACOS_DEPLOYMENT_TARGET: "11.0"
  CMAKE_VERSION: "3.29.6"
  NINJA_VERSION: "1.12.1"

jobs:
  build:
    name: ${{ matrix.config.name }} (Qt ${{ matrix.qt_config.qt_version }}, QtC ${{ matrix.qt_config.qt_creator_version }})
    runs-on: ${{ matrix.config.os }}
    outputs:
        tag: ${{ steps.git.outputs.tag }}
    strategy:
      matrix:
        config:
        - {
            name: "Windows Latest MSVC", artifact: "Windows-x64",
            os: windows-latest,
            platform: windows_x64,
            cc: "cl", cxx: "cl",
            environment_script: "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat",
          }
        - {
            name: "Ubuntu 22.04 GCC", artifact: "Linux-x64",
            os: ubuntu-22.04,
            platform: linux_x64,
            cc: "gcc", cxx: "g++"
          }
        - {
            name: "macOS Latest Clang", artifact: "macOS-universal",
            os: macos-latest,
            platform: mac_x64,
            cc: "clang", cxx: "clang++"
          }
        qt_config:
        - {
            qt_version: "6.10.1",
            qt_creator_version: "18.0.2"
          }
        - {
            qt_version: "6.10.3",
            qt_creator_version: "19.0.2"
          }

    steps:
    - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955
      with:
        submodules: recursive

    - name: Checkout submodules
      id: git
      shell: cmake -P {0}
      run: |
        if (${{github.ref}} MATCHES "tags/v(.*)")
            file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${CMAKE_MATCH_1}")
        else()
            execute_process(
                COMMAND git rev-parse --short HEAD
                OUTPUT_VARIABLE short_sha
                OUTPUT_STRIP_TRAILING_WHITESPACE
            )
            file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${short_sha}")
        endif()

    - name: Download Ninja and CMake
      uses: lukka/get-cmake@2ecc21724e5215b0e567bc399a2602d2ecb48541
      with:
        cmakeVersion: ${{ env.CMAKE_VERSION }}
        ninjaVersion: ${{ env.NINJA_VERSION }}

    - name: Install dependencies
      shell: cmake -P {0}
      run: |
        if ("${{ runner.os }}" STREQUAL "Linux")
          execute_process(
            COMMAND sudo apt update
          )
          execute_process(
            COMMAND sudo apt install
              # build dependencies
              libgl1-mesa-dev libgtest-dev libgmock-dev
              # runtime dependencies for tests (Qt is downloaded outside package manager,
              # thus minimal dependencies must be installed explicitly)
              libsecret-1-0 libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-render0
              libxcb-shape0 libxcb-shm0 libxcb-sync1 libxcb-xfixes0 libxcb-xkb1 libxkbcommon-x11-0 xvfb
            RESULT_VARIABLE result
          )
          if (NOT result EQUAL 0)
            message(FATAL_ERROR "Failed to install dependencies")
          endif()
        endif()

    - name: Download Qt
      id: qt
      shell: cmake -P {0}
      run: |
        set(qt_version "${{ matrix.qt_config.qt_version }}")
        set(qt_creator_version "${{ matrix.qt_config.qt_creator_version }}")

        string(REPLACE "." "" qt_version_dotless "${qt_version}")
        if ("${{ runner.os }}" STREQUAL "Windows")
          set(url_os "windows_x86")
          set(qt_package_arch_suffix "win64_msvc2022_64")
          set(qt_dir_prefix "${qt_version}/msvc2022_64")
          if (qt_creator_version VERSION_GREATER_EQUAL "18.0.0")
            set(qt_package_suffix "-Windows-Windows_11_24H2-MSVC2022-Windows-Windows_11_24H2-X86_64")
          else()
            set(qt_package_suffix "-Windows-Windows_11_23H2-MSVC2022-Windows-Windows_11_23H2-X86_64")
          endif()
        elseif ("${{ runner.os }}" STREQUAL "Linux")
          set(url_os "linux_x64")
          if (qt_version VERSION_LESS "6.7.0")
            set(qt_package_arch_suffix "gcc_64")
          else()
            set(qt_package_arch_suffix "linux_gcc_64")
          endif()
          set(qt_dir_prefix "${qt_version}/gcc_64")
          if (qt_creator_version VERSION_GREATER_EQUAL "18.0.0")
            set(qt_package_suffix "-Linux-RHEL_9_4-GCC-Linux-RHEL_9_4-X86_64")
          else()
            set(qt_package_suffix "-Linux-RHEL_8_10-GCC-Linux-RHEL_8_10-X86_64")
          endif()
        elseif ("${{ runner.os }}" STREQUAL "macOS")
          set(url_os "mac_x64")
          set(qt_package_arch_suffix "clang_64")
          set(qt_dir_prefix "${qt_version}/macos")
          if (qt_version VERSION_LESS "6.9.1")
            set(qt_package_suffix "-MacOS-MacOS_14-Clang-MacOS-MacOS_14-X86_64-ARM64")
          else()
            set(qt_package_suffix "-MacOS-MacOS_15-Clang-MacOS-MacOS_15-X86_64-ARM64")
          endif()
        endif()

        set(qt_base_url "https://download.qt.io/online/qtsdkrepository/${url_os}/desktop/qt6_${qt_version_dotless}/qt6_${qt_version_dotless}")
        file(DOWNLOAD "${qt_base_url}/Updates.xml" ./Updates.xml SHOW_PROGRESS)

        file(READ ./Updates.xml updates_xml)
        string(REGEX MATCH "<Name>qt.qt6.*<Version>([0-9+-.]+)</Version>" updates_xml_output "${updates_xml}")
        set(qt_package_version ${CMAKE_MATCH_1})

        file(MAKE_DIRECTORY qt6)

        # Save the path for other steps
        file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qt6" qt_dir)
        file(APPEND "$ENV{GITHUB_OUTPUT}" "qt_dir=${qt_dir}")

        message("Downloading Qt to ${qt_dir}")
        function(downloadAndExtract url archive)
          message("Downloading ${url}")
          file(DOWNLOAD "${url}" ./${archive} SHOW_PROGRESS)
          execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../${archive} WORKING_DIRECTORY qt6)
        endfunction()

        foreach(package qtbase qtdeclarative qttools qtsvg)
          downloadAndExtract(
            "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
            ${package}.7z
          )
        endforeach()

        foreach(package qt5compat qtshadertools)
          downloadAndExtract(
            "${qt_base_url}/qt.qt6.${qt_version_dotless}.addons.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
            ${package}.7z
          )
        endforeach()

        function(downloadAndExtractLibicu url archive)
          message("Downloading ${url}")
          file(DOWNLOAD "${url}" ./${archive} SHOW_PROGRESS)
          execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../../${archive} WORKING_DIRECTORY qt6/lib)
        endfunction()

        # uic depends on libicu*.so
        if ("${{ runner.os }}" STREQUAL "Linux")
          if (qt_version VERSION_LESS "6.7.0")
            set(uic_suffix "Rhel7.2-x64")
          else()
            set(uic_suffix "Rhel8.6-x86_64")
          endif()
          downloadAndExtractLibicu(
            "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}icu-linux-${uic_suffix}.7z"
            icu.7z
          )
        endif()

    - name: Download Qt Creator
      uses: qt-creator/install-dev-package@1460787a21551eb3d867b0de30e8d3f1aadef5ac
      with:
        version: ${{ matrix.qt_config.qt_creator_version }}
        unzip-to: 'qtcreator'
        platform: ${{ matrix.config.platform }}

    - name: Extract Qt Creator
      id: qt_creator
      shell: cmake -P {0}
      run: |
        file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qtcreator" qtc_dir)
        file(APPEND "$ENV{GITHUB_OUTPUT}" "qtc_dir=${qtc_dir}")

    - name: Build
      shell: cmake -P {0}
      run: |
        set(ENV{CC} ${{ matrix.config.cc }})
        set(ENV{CXX} ${{ matrix.config.cxx }})
        set(ENV{MACOSX_DEPLOYMENT_TARGET} "${{ env.MACOS_DEPLOYMENT_TARGET }}")

        if ("${{ runner.os }}" STREQUAL "Windows" AND NOT "x${{ matrix.config.environment_script }}" STREQUAL "x")
          execute_process(
            COMMAND "${{ matrix.config.environment_script }}" && set
            OUTPUT_FILE environment_script_output.txt
          )
          file(STRINGS environment_script_output.txt output_lines)
          foreach(line IN LISTS output_lines)
            if (line MATCHES "^([a-zA-Z0-9_-]+)=(.*)$")
              set(ENV{${CMAKE_MATCH_1}} "${CMAKE_MATCH_2}")
            endif()
          endforeach()
        endif()

        set(ENV{NINJA_STATUS} "[%f/%t %o/sec] ")
        if ("${{ runner.os }}" STREQUAL "macOS")
            set(ENV{CMAKE_OSX_ARCHITECTURES} "x86_64;arm64")
        endif()

        set(build_plugin_py "scripts/build_plugin.py")
        foreach(dir "share/qtcreator/scripts" "Qt Creator.app/Contents/Resources/scripts" "Contents/Resources/scripts")
          if(EXISTS "${{ steps.qt_creator.outputs.qtc_dir }}/${dir}/build_plugin.py")
            set(build_plugin_py "${dir}/build_plugin.py")
            break()
          endif()
        endforeach()

        execute_process(
          COMMAND python
            -u
            "${{ steps.qt_creator.outputs.qtc_dir }}/${build_plugin_py}"
            --name "$ENV{PLUGIN_NAME}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.qt_creator_version }}-${{ matrix.config.artifact }}"
            --src .
            --build build
            --qt-path "${{ steps.qt.outputs.qt_dir }}"
            --qtc-path "${{ steps.qt_creator.outputs.qtc_dir }}"
            --output-path "$ENV{GITHUB_WORKSPACE}"
          RESULT_VARIABLE result
        )
        if (NOT result EQUAL 0)
          string(REGEX MATCH "FAILED:.*$" error_message "${output}")
          string(REPLACE "\n" "%0A" error_message "${error_message}")
          message("::error::${error_message}")
          message(FATAL_ERROR "Build failed")
        endif()

    - name: Upload
      uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
      with:
        path: ./${{ env.PLUGIN_NAME }}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.qt_creator_version }}-${{ matrix.config.artifact }}.7z
        name: ${{ env.PLUGIN_NAME}}-v${{ steps.git.outputs.tag }}-QtC${{ matrix.qt_config.qt_creator_version }}-${{ matrix.config.artifact }}.7z

    - name: Run unit tests
      if: startsWith(matrix.config.os, 'ubuntu')
      run: |
        xvfb-run ./build/build/test/QodeAssistTest

  release:
    if: contains(github.ref, 'tags/v')
    runs-on: ubuntu-22.04
    needs: [build]

    steps:
    - name: Download artifacts
      uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
      with:
        path: release-with-dirs

    - name: Fixup artifacts
      run: |
        mkdir release
        mv release-with-dirs/*/* release/

    - name: Download QodeAssistUpdater
      run: |
        # Get latest release info and download assets
        LATEST_RELEASE=$(curl -s https://api.github.com/repos/Palm1r/QodeAssistUpdater/releases/latest)
        
        # Download all assets except .sha256 files
        echo "$LATEST_RELEASE" | jq -r '.assets[].browser_download_url' | grep -v '\.sha256$' | while read url; do
          filename=$(basename "$url")
          echo "Downloading $filename..."
          curl -L -o "release/$filename" "$url"
        done

    - name: Create Release
      id: create_release
      uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: v${{ needs.build.outputs.tag }}
        files: release/*
        draft: false
        prerelease: false
