name: D++ CI
on:
  push:
    paths:
      - '**Dockerfile'
      - '**.cxx'
      - '**.cpp'
      - '**.h'
      - '**.hpp'
      - '**.cmake'
      - '**ci.yml'
      - '**CMakeLists.txt'
      - '!**/docpages/**' # Never allow docpages to build CI from doc PRs.
  pull_request:
    paths:
      - '**Dockerfile'
      - '**.cxx'
      - '**.cpp'
      - '**.h'
      - '**.hpp'
      - '**.cmake'
      - '**ci.yml'
      - '**CMakeLists.txt'
      - '!**/docpages/**'

permissions:
  contents: read

jobs:
  linux:
    permissions:
      contents: write
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.cfg.arch }}-(${{ matrix.cfg.cpp }}-${{ matrix.cfg.version }})
      cancel-in-progress: true
    name: Linux ${{ matrix.cfg.arch }} (${{ matrix.cfg.cpp }}-${{ matrix.cfg.version }}${{ matrix.cfg.name-extra }})
    runs-on: ${{ matrix.cfg.os }}
    strategy:
      fail-fast: false # Don't fail everything if one fails. We want to test each OS/Compiler individually
      matrix:
        # GitHub hosted runners on Azure
        # arm7hf is a self-hosted docker-based runner at Brainbox.cc. Raspberry Pi 4, 8gb 4-core with NEON
        cfg:
          # clang++
          - { arch: 'amd64', concurrency: 4, os: ubuntu-22.04, package: clang-11, cpp: clang++, version: 11, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-22.04, package: clang-12, cpp: clang++, version: 12, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-22.04, package: clang-13, cpp: clang++, version: 13, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-22.04, package: clang-14, cpp: clang++, version: 14, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-22.04, package: clang-15, cpp: clang++, version: 15, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-24.04, package: clang-16, cpp: clang++, version: 16, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-24.04, package: clang-17, cpp: clang++, version: 17, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-24.04, package: clang-18, cpp: clang++, version: 18, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-24.04, package: clang-19, cpp: clang++, version: 19, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-24.04, package: clang-20, cpp: clang++, version: 20, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-24.04, package: 'clang-21 libc++-21-dev libc++abi-21-dev', cpp: clang++, version: 21, cmake-flags: '-DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++"', cpack: 'no', ctest: 'no', mold: 'yes', name-extra: ' libc++', llvm-apt: 'yes' }
          # g++
          - { arch: 'amd64', concurrency: 4, os: ubuntu-22.04, package: g++-10, cpp: g++, version: 10, cmake-flags: '-DDPP_NO_CORO=ON', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-22.04, package: g++-11, cpp: g++, version: 11, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-22.04, package: g++-12, cpp: g++, version: 12, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-24.04, package: g++-13, cpp: g++, version: 13, cmake-flags: '', cpack: 'yes', ctest: 'yes', mold: 'yes' }
          - { arch: 'amd64', concurrency: 4, os: ubuntu-24.04, package: g++-14, cpp: g++, version: 14, cmake-flags: '', cpack: 'no', ctest: 'no', mold: 'yes' }
          # Self hosted
          - { arch: 'arm7hf', concurrency: 4, os: [self-hosted, linux, ARM], package: g++-12, cpp: g++, version: 12, cmake-flags: '', cpack: 'yes', ctest: 'no', mold: 'no' }
          - { arch: 'arm64', concurrency: 4, os: [self-hosted, linux, ARM64], package: g++-12, cpp: g++, version: 12, cmake-flags: '', cpack: 'yes', ctest: 'no', mold: 'yes' }
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit

      - name: Checkout D++
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

      - name: Setup mold
        if: ${{ matrix.cfg.mold == 'yes' }}
        uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1

      - name: Add LLVM apt repository
        if: ${{ matrix.cfg.llvm-apt }}
        run: |
          osname=`cat /etc/os-release | grep -e "^VERSION_CODENAME" | cut -d= -f2`
          wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
          sudo apt-add-repository -y "deb http://apt.llvm.org/$osname/ llvm-toolchain-$osname-${{ matrix.cfg.version }} main"

      - name: Install apt packages
        run: sudo sed -i 's/azure\.//' /etc/apt/sources.list && sudo apt update && sudo apt-get install -y ${{ matrix.cfg.package }} pkg-config libopus-dev zlib1g-dev rpm

      - name: Generate CMake
        run: cmake -B build -DDPP_NO_VCPKG=ON -DAVX_TYPE=AVX0 -DCMAKE_BUILD_TYPE=Release ${{matrix.cfg.cmake-flags}}
        env:
          CXX: ${{ matrix.cfg.cpp }}-${{ matrix.cfg.version }}

      - name: Build Project
        run: cd build && make -j${{ matrix.cfg.concurrency }}

      - name: Run unit tests
        if: ${{ matrix.cfg.ctest == 'yes' }}
        run: cd build/library && ./unittest
        env:
          DPP_UNIT_TEST_TOKEN: ${{secrets.DPP_UNIT_TEST_TOKEN}}
          TEST_GUILD_ID: ${{secrets.TEST_GUILD_ID}}
          TEST_TEXT_CHANNEL_ID: ${{secrets.TEST_TEXT_CHANNEL_ID}}
          TEST_VC_ID: ${{secrets.TEST_VC_ID}}
          TEST_USER_ID: ${{secrets.TEST_USER_ID}}
          TEST_EVENT_ID: ${{secrets.TEST_EVENT_ID}}

      - name: Package distributable
        if: ${{ matrix.cfg.cpack == 'yes' }}
        run: cd build && cpack --verbose

      - name: Upload Binary (DEB)
        if: ${{ matrix.cfg.cpack == 'yes' }}
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "libdpp - Debian Package ${{matrix.cfg.arch}}"
          path: '${{github.workspace}}/build/*.deb'

      - name: Upload Binary (RPM)
        if: ${{ matrix.cfg.cpack == 'yes' }}
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "libdpp - RPM Package ${{matrix.cfg.arch}}"
          path: '${{github.workspace}}/build/*.rpm'

  macos:
    permissions:
      contents: write
    name: macOS ${{ matrix.cfg.arch }} (${{ matrix.cfg.cpp }}-${{ matrix.cfg.version }})
    runs-on: ${{ matrix.cfg.os }}
    strategy:
      fail-fast: false # Don't fail everything if one fails. We want to test each OS/Compiler individually
      matrix:
        cfg:
          - { arch: 'arm64', concurrency: 3, os: macos-latest, cpp: clang++, version: 26, cmake-flags: '', xcode-version: '26' }
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit

      - name: Checkout D++
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

      - name: Update Xcode
        uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
        with:
          xcode-version: ${{ matrix.cfg.xcode-version }}

      - name: Install homebrew packages
        run: brew install make opus openssl

      - name: Generate CMake
        run: cmake -B build -DDPP_NO_VCPKG=ON -DCMAKE_BUILD_TYPE=Release -DDPP_CORO=ON -DAVX_TYPE=AVX0
        env:
          DONT_RUN_VCPKG: true

      - name: Build Project
        run: cmake --build build -j${{ matrix.cfg.concurrency }}
        env:
          DONT_RUN_VCPKG: true

      - name: Run offline unit tests
        run: cd build/library && ./unittest

  windows: # Windows x64 and x86 build matrix
    permissions:
      contents: write
    strategy:
      fail-fast: false # Don't cancel other matrix jobs if one fails
      matrix:
        cfg:
        - { name: 'x64',            arch: x64, config: Release, vs: '2022', os: 'windows-2022', vsv: '17', upload: true,  options: '' }
        - { name: 'x64',            arch: x64, config: Debug,   vs: '2022', os: 'windows-2022', vsv: '17', upload: true,  options: '' }
        - { name: 'x86',            arch: x86, config: Release, vs: '2022', os: 'windows-2022', vsv: '17', upload: true,  options: '-T host=x86' }
        - { name: 'x86',            arch: x86, config: Debug,   vs: '2022', os: 'windows-2022', vsv: '17', upload: true,  options: '-T host=x86' }
        - { name: 'x64-Clang',      arch: x64, config: Debug,   vs: '2022', os: 'windows-2022', vsv: '17', upload: false, options: '-T ClangCL' }

    name: "Windows ${{matrix.cfg.name}}-${{matrix.cfg.config}}-vs${{matrix.cfg.vs}}"
    runs-on: ${{matrix.cfg.os}}
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit

      - name: Checkout D++
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          path: main

      - name: Add MSBuild to PATH
        uses: microsoft/setup-msbuild@30375c66a4eea26614e0d39710365f22f8b0af57 # v3.0.0

      - name: Install chocolatey packages ${{ matrix.cfg.arch}}
        uses: seanmiddleditch/gha-setup-ninja@3b1f8f94a2f8254bd26914c4ab9474d4f0015f67 # master

      - name: Generate CMake (x64)
        if: ${{ matrix.cfg.arch == 'x64' }}
        run: mkdir main/build && cd main/build && cmake -G "Visual Studio ${{matrix.cfg.vsv}} ${{matrix.cfg.vs}}" -DDPP_NO_VCPKG=ON -DAVX_TYPE=AVX0 -DDPP_USE_PCH=on ${{matrix.cfg.options}} ..
        env:
          DONT_RUN_VCPKG: true

      - name: Generate CMake (x86)
        if: ${{ matrix.cfg.arch == 'x86' }}
        run: mkdir main/build && cd main/build && cmake -DCMAKE_TOOLCHAIN_FILE="cmake\Win32Toolchain.cmake" -DDPP_NO_VCPKG=ON -DAVX_TYPE=AVX0 -DDPP_USE_PCH=on -G "Visual Studio ${{matrix.cfg.vsv}} ${{matrix.cfg.vs}}" -A Win32 ${{matrix.cfg.options}} ..
        env:
          DONT_RUN_VCPKG: true

      - name: Build Project
        run: cmake --build main/build --target dpp --config ${{matrix.cfg.config}} --parallel 2
        env:
          DONT_RUN_VCPKG: true

      - name: Move debug files for packaging
        if: ${{ matrix.cfg.config == 'Debug' }}
        run: xcopy main\build\library\Debug\* main\build\library\Release\ /s /q

      - name: Package distributable
        run: cd main/build && cpack --verbose
        env:
          DONT_RUN_VCPKG: true

      - name: Upload Binary
        if: ${{ matrix.cfg.upload }}
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "libdpp - Windows ${{matrix.cfg.name}}-${{matrix.cfg.config}}-vs${{matrix.cfg.vs}}"
          path: '${{github.workspace}}/main/build/*.zip'

  cross-compiles:
    permissions:
      contents: write
    strategy:
      fail-fast: false
      matrix:
        cfg:
          # Replaced with self-hosted runner
          - {name: "Linux x86", os: ubuntu-24.04, cmake-options: -DCMAKE_TOOLCHAIN_FILE=cmake/LINUXx86ToolChain.cmake -DBUILD_VOICE_SUPPORT=OFF}
          - {name: "ARMv6", os: ubuntu-24.04, cmake-options: -DDPP_NO_CORO=ON -DCMAKE_TOOLCHAIN_FILE=cmake/ARMv6ToolChain.cmake}

    name: ${{matrix.cfg.name}}
    runs-on: ${{matrix.cfg.os}}
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit

      - name: Checkout D++
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

      - name: Install Packages
        run: sudo sed -i 's/azure\.//' /etc/apt/sources.list && sudo apt update && sudo apt-get install -y cmake rpm

      - name: Generate CMakeFiles
        run: mkdir build && cd build && sudo cmake ${{matrix.cfg.cmake-options}} -DDPP_NO_VCPKG=ON -DCMAKE_BUILD_TYPE=Release -DAVX_TYPE=AVX0 ..

      - name: Compile Source
        run: cd build && sudo make -j2

      - name: Package Distributable
        run: cd build && sudo cpack --verbose || cat /home/runner/work/DPP/DPP/build/_CPack_Packages/Linux/DEB/PreinstallOutput.log

      - name: Upload Binaries (DEB)
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "libdpp - Debian Package ${{matrix.cfg.name}}"
          path: "${{github.workspace}}/build/*.deb"

      - name: Upload Binaries (RPM)
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "libdpp - RPM Package ${{matrix.cfg.name}}"
          path: "${{github.workspace}}/build/*.rpm"

  # Static library build (BUILD_SHARED_LIBS=OFF)
  static-linux:
    permissions:
      contents: read
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}-static-${{ matrix.cfg.cpp }}-${{ matrix.cfg.version }}
      cancel-in-progress: true
    name: Static Linux (${{ matrix.cfg.cpp }}-${{ matrix.cfg.version }})
    runs-on: ${{ matrix.cfg.os }}
    strategy:
      fail-fast: false
      matrix:
        cfg:
          - { os: ubuntu-24.04, package: g++-13, cpp: g++, version: 13, concurrency: 4 }
          - { os: ubuntu-24.04, package: clang-20, cpp: clang++, version: 20, concurrency: 4 }
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit

      - name: Checkout D++
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

      - name: Setup mold
        uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1

      - name: Install apt packages
        run: sudo sed -i 's/azure\.//' /etc/apt/sources.list && sudo apt update && sudo apt-get install -y ${{ matrix.cfg.package }} pkg-config libopus-dev zlib1g-dev libssl-dev rpm

      - name: Generate CMake
        run: cmake -B build -DBUILD_SHARED_LIBS=OFF -DDPP_NO_VCPKG=ON -DAVX_TYPE=AVX0 -DCMAKE_BUILD_TYPE=Release
        env:
          CXX: ${{ matrix.cfg.cpp }}-${{ matrix.cfg.version }}

      - name: Build Project
        run: cd build && make -j${{ matrix.cfg.concurrency }}

  # Install D++ and consume it from a separate project via find_package(dpp)
  install-consumer:
    permissions:
      contents: read
    name: Install & find_package(dpp)
    runs-on: ubuntu-24.04
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit

      - name: Checkout D++
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

      - name: Setup mold
        uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1

      - name: Install apt packages
        run: sudo sed -i 's/azure\.//' /etc/apt/sources.list && sudo apt update && sudo apt-get install -y g++-13 pkg-config libopus-dev zlib1g-dev libssl-dev

      - name: Build and install D++
        run: |
          cmake -B build -DDPP_NO_VCPKG=ON -DAVX_TYPE=AVX0 -DCMAKE_BUILD_TYPE=Release -DDPP_BUILD_TEST=OFF -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/_install"
          cmake --build build -j$(nproc)
          cmake --install build
        env:
          CXX: g++-13

      - name: Consume D++ via find_package
        run: |
          mkdir -p consumer && cd consumer
          cat > CMakeLists.txt <<'EOF'
          cmake_minimum_required(VERSION 3.16)
          project(dpp_consumer CXX)
          find_package(dpp REQUIRED)
          add_executable(consumer main.cpp)
          target_link_libraries(consumer dpp::dpp)
          EOF
          cat > main.cpp <<'EOF'
          #include <dpp/dpp.h>
          int main() { dpp::cluster bot("token"); return 0; }
          EOF
          cmake -B build -DCMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/_install" -DCMAKE_BUILD_TYPE=Release
          cmake --build build -j$(nproc)
        env:
          CXX: g++-13
