name: Quality Assurance

on:
  push:
  pull_request:
  
jobs:
  build-linux:
    name: Linux (${{ matrix.platform.label }}, ${{ matrix.compiler.label }}, ${{ matrix.configure.label }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-22.04]
        platform:
          - {label: "x64", arch: "amd64", configure: ""}
          - {label: "x32", arch: "i386", configure: "PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32"}
        compiler:
          - {label: "gcc", cc: "gcc", cxx: "g++"}
          - {label: "clang", cc: "clang", cxx: "clang++"}
        configure:
          - {label: "with parser generation", opt: "--enable-parser-generation" }
          - {label: "wo curl",    opt: "--without-curl" }
          - {label: "wo lua",     opt: "--without-lua" }
          - {label: "wo maxmind", opt: "--without-maxmind" }
          - {label: "wo libxml",  opt: "--without-libxml" }
          - {label: "wo geoip",   opt: "--without-geoip" }
          - {label: "wo ssdeep",  opt: "--without-ssdeep" }
          - {label: "with lmdb",  opt: "--with-lmdb" }
          - {label: "with pcre", opt: "--with-pcre" }
        exclude:
          - platform: {label: "x32"}
            configure: {label: "wo geoip"}
          - platform: {label: "x32"}
            configure: {label: "wo ssdeep"}
    steps:
      - name: Detect latest Lua dev package
        id: detect_lua
        shell: bash
        run: |
          set -euo pipefail

          sudo apt-get update -y -qq

          CANDIDATES="$(apt-cache pkgnames | grep -E '^liblua[0-9]+\.[0-9]+-dev$' || true)"

          if [ -z "$CANDIDATES" ]; then
            echo "No libluaX.Y-dev package found"
            exit 1
          fi

          BEST_PKG="$(
            printf '%s\n' "$CANDIDATES" \
            | sed -E 's/^liblua([0-9]+\.[0-9]+)-dev$/\1 &/' \
            | sort -V \
            | tail -n1 \
            | awk '{print $2}'
          )"

          if [ -z "$BEST_PKG" ]; then
            echo "Failed to determine Lua package"
            exit 1
          fi

          echo "lua_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT"
          echo "Using $BEST_PKG"

      - name: Setup Dependencies (common)
        run: |
          sudo dpkg --add-architecture ${{ matrix.platform.arch }}
          sudo apt-get update -y -qq
          sudo apt-get install -y libyajl-dev:${{ matrix.platform.arch }} \
                                  libcurl4-openssl-dev:${{ matrix.platform.arch }} \
                                  liblmdb-dev:${{ matrix.platform.arch }} \
                                  ${{ steps.detect_lua.outputs.lua_pkg }}:${{ matrix.platform.arch }} \
                                  libmaxminddb-dev:${{ matrix.platform.arch }} \
                                  libpcre2-dev:${{ matrix.platform.arch }} \
                                  pcre2-utils:${{ matrix.platform.arch }} \
                                  bison flex python3 python3-venv
      - name: Setup Dependencies (x32)
        if: ${{ matrix.platform.label == 'x32' }}
        run: |
          sudo apt-get install g++-multilib
          sudo apt-get install -y libxml2-dev:${{ matrix.platform.arch }} \
                                  libpcre3-dev:${{ matrix.platform.arch }}
      - name: Setup Dependencies (x64)
        if: ${{ matrix.platform.label == 'x64' }}
        run: |
          sudo apt-get install -y libfuzzy-dev:${{ matrix.platform.arch }}
          
      - uses: actions/checkout@v6
        with:
          submodules: recursive
          fetch-depth: 0
      - name: build.sh
        run: ./build.sh
      - name: configure
        env:
          CC: ${{ matrix.compiler.cc }}
          CXX: ${{ matrix.compiler.cxx }}
        run: ./configure ${{ matrix.platform.configure }} ${{ matrix.configure.opt }} --enable-assertions=yes
      - uses: ammaraskar/gcc-problem-matcher@master
      - name: make
        run: make -j `nproc`
      - name: check
        run: make check

  build-macos:
    name: macOS (${{ matrix.configure.label }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-14]
        configure:
          - {label: "with parser generation", opt: "--enable-parser-generation" }
          - {label: "wo curl",    opt: "--without-curl" }
          - {label: "wo lua",     opt: "--without-lua" }
          - {label: "wo maxmind", opt: "--without-maxmind" }
          - {label: "wo libxml",  opt: "--without-libxml" }
          - {label: "wo geoip",   opt: "--without-geoip" }
          - {label: "wo ssdeep",  opt: "--without-ssdeep" }
          - {label: "with lmdb",  opt: "--with-lmdb" }
          - {label: "with pcre", opt: "--with-pcre" }
    steps:
      - name: Setup Dependencies
        # curl, pcre2 not installed because they're already
        # included in the image
        run: |
          brew install autoconf \
                       automake \
                       libtool \
                       yajl \
                       lmdb \
                       lua \
                       libmaxminddb \
                       libxml2 \
                       ssdeep \
                       pcre \
                       bison \
                       flex 
                       
      - uses: actions/checkout@v6
        with:
          submodules: recursive
          fetch-depth: 0
      - name: build.sh
        run: ./build.sh
      - name: configure
        run: ./configure ${{ matrix.configure.opt }} --enable-assertions=yes
      - uses: ammaraskar/gcc-problem-matcher@master
      - name: make
        run: make -j `sysctl -n hw.logicalcpu`
      - name: check
        run: make check

  build-windows:
    name: Windows (${{ matrix.platform.label }}, ${{ matrix.configure.label }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [windows-2022]
        platform:
          - {label: "x64", arch: "x86_64"}
        configuration: [Release]
        configure:
          - {label: "full",       opt: "" }
          - {label: "wo curl",    opt: "-DWITH_CURL=OFF" }
          - {label: "wo lua",     opt: "-DWITH_LUA=OFF" }
          - {label: "wo maxmind", opt: "-DWITH_MAXMIND=OFF" }
          - {label: "wo libxml",  opt: "-DWITH_LIBXML2=OFF" }
          - {label: "with lmdb",  opt: "-DWITH_LMDB=ON" }
    steps:
      - uses: actions/checkout@v6
        with:
          submodules: recursive
          fetch-depth: 0
      - name: Install Conan
        run: |
          pip3 install conan --upgrade
          conan profile detect
      - uses: ammaraskar/msvc-problem-matcher@master
      - name: Build ${{ matrix.configuration }} ${{ matrix.platform.arch }} ${{ matrix.configure.label }}
        shell: cmd
        run: vcbuild.bat ${{ matrix.configuration }} ${{ matrix.platform.arch }} NO_ASAN "${{ matrix.configure.opt }}"
      - name: Set up test environment
        working-directory: build\win32\build\${{ matrix.configuration }}
        env:
          BASE_DIR: ..\..\..\..
        shell: cmd
        run: |
          copy unit_tests.exe %BASE_DIR%\test
          copy regression_tests.exe %BASE_DIR%\test
          copy libModSecurity.dll %BASE_DIR%\test
          copy %BASE_DIR%\unicode.mapping %BASE_DIR%\test
          md \tmp
          md \bin
          copy "C:\Program Files\Git\usr\bin\echo.exe" \bin
          copy "C:\Program Files\Git\usr\bin\echo.exe" \bin\echo
      - name: Disable tests that don't work on Windows
        working-directory: test\test-cases\regression
        shell: cmd
        run: |
          jq "map(if .title == \"Test match variable (1/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json
          jq "map(if .title == \"Test match variable (2/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json
          jq "map(if .title == \"Test match variable (3/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json
          jq "map(if .title == \"Variable offset - FILES_NAMES\" then .enabled = 0 else . end)" offset-variable.json > tmp.json && move /Y tmp.json offset-variable.json
      - name: Run tests
        working-directory: build\win32\build
        run: |
          ctest -C ${{ matrix.configuration }} --output-on-failure

  cppcheck:
    runs-on: [macos-14]
    steps:
      - name: Setup Dependencies
        run: |
          brew install autoconf \
                       automake \
                       libtool \
                       cppcheck
                       
      - uses: actions/checkout@v6
        with:
          submodules: recursive
          fetch-depth: 0
      - name: configure
        run: |
          ./build.sh
          ./configure
      - name: cppcheck
        run: make check-static
