name: CI

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
  schedule:
    - cron: '11 1 * * 0'

jobs:
  build:
    runs-on: ubuntu-latest

    env:
      CI: true

    strategy:
      matrix:
        compiler:
          - clang
          - gcc
        container:
          - almalinux:8
          - alpine:3.15
          - ubuntu:18.04

    container: ${{ matrix.container }}

    steps:
      - name: Checkout ProFTPD
        uses: actions/checkout@v3
        with:
          repository: proftpd/proftpd
          path: proftpd

      - name: Checkout module source code
        uses: actions/checkout@v3
        with:
          path: proftpd/contrib/mod_proxy

      - name: Whitespace check
        if: ${{ matrix.container == 'ubuntu:18.04' }}
        run: |
          apt-get update -qq
          apt-get install -y git
          cd proftpd/contrib/mod_proxy
          if [[ -n $(git diff --check HEAD^) ]]; then
            echo "You must remove whitespace before submitting a pull request"
            echo ""
            git diff --check HEAD^
            exit 1
          fi

      - name: Install Alpine packages
        if: ${{ matrix.container == 'alpine:3.15' }}
        run: |
          apk update
          # for builds
          apk add bash build-base clang compiler-rt-static gcc make zlib-dev
          # for unit tests
          apk add check check-dev subunit subunit-dev
          # for Redis support
          apk add hiredis-dev
          # for OpenSSL support
          apk add openssl openssl-dev
          # for SQLite support
          apk add sqlite sqlite-dev
          # for debugging
          clang --version
          gcc --version
          openssl version -a

      - name: Install RPM packages
        if: ${{ matrix.container == 'almalinux:8' }}
        run: |
          # Need to add other repos for e.g. libsodium
          yum install -y dnf-plugins-core epel-release clang gcc make zlib-devel
          # for unit tests
          yum install -y check-devel https://cbs.centos.org/kojifiles/packages/subunit/1.4.0/1.el8/x86_64/subunit-1.4.0-1.el8.x86_64.rpm https://cbs.centos.org/kojifiles/packages/subunit/1.4.0/1.el8/x86_64/subunit-devel-1.4.0-1.el8.x86_64.rpm
          # for Redis support
          yum install -y hiredis-devel
          # for OpenSSL support
          yum install -y openssl openssl-devel
          # for SQLite support
          yum install -y sqlite-devel
          # for debugging
          clang --version
          gcc --version
          openssl version -a

      - name: Install Ubuntu packages
        if: ${{ matrix.container == 'ubuntu:18.04' }}
        run: |
          apt-get update -qq
          # for builds
          apt-get install -y clang gcc make
          # for unit tests
          apt-get install -y check libsubunit-dev

          # for Redis support 
          apt-get install -y libhiredis-dev
          # for OpenSSL support
          apt-get install -y libssl-dev
          # for SQLite support
          apt-get install -y libsqlite3-dev sqlite3

          # for test code coverage
          apt-get install -y lcov ruby
          gem install coveralls-lcov
          # for HTML validation
          apt-get install -y tidy
          # for debugging
          clang --version
          gcc --version
          openssl version -a

      - name: Prepare code coverage
        if: ${{ matrix.container == 'ubuntu:18.04' }}
        run: |
          lcov --directory proftpd --zerocounters

      - name: Build without Redis, SSL support
        env:
          CC: ${{ matrix.compiler }}
        run: |
          cd proftpd
          ./configure LIBS="-lm -lsubunit -lrt -pthread" --enable-devel=coverage --enable-tests --with-modules=mod_proxy
          make

      - name: Build with Redis, without SSL support
        env:
          CC: ${{ matrix.compiler }}
        run: |
          cd proftpd
          make clean
          ./configure LIBS="-lm -lsubunit -lrt -pthread" --enable-devel=coverage --enable-redis --enable-tests --with-modules=mod_proxy
          make

      - name: Build with Redis, SSL support as shared module
        env:
          CC: ${{ matrix.compiler }}
        run: |
          cd proftpd
          make clean
          ./configure LIBS="-lm -lsubunit -lrt -pthread" --enable-devel=coverage --enable-dso --enable-redis --enable-tests --with-shared=mod_tls:mod_proxy
          make

      - name: Build with Redis, SSL support as static module
        env:
          CC: ${{ matrix.compiler }}
        run: |
          cd proftpd
          make clean
          ./configure LIBS="-lm -lsubunit -lrt -pthread" --enable-devel=coverage --enable-redis --enable-tests --with-modules=mod_tls:mod_proxy
          make

      - name: Run unit tests
        env:
          CC: ${{ matrix.compiler }}
        # Note: Skip the unit tests on Alpine
        if: ${{ matrix.container != 'alpine:3.15' }}
        run: |
          cd proftpd/contrib/mod_proxy
          make TEST_VERBOSE=1 check

      - name: Install with static modules
        run: |
          cd proftpd
          make install

      - name: Build with shared modules
        env:
          CC: ${{ matrix.compiler }}
        run: |
          cd proftpd
          make clean
          ./configure LIBS="-lm -lsubunit -lrt -pthread" --enable-devel --enable-dso --with-shared=mod_proxy
          make

      - name: Install with shared modules
        run: |
          cd proftpd
          make install

      # https://github.com/google/sanitizers/wiki/AddressSanitizer
      # https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer
      # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
      #
      # NOTE: Using MemorySanitizer is desirable, but currently unusable since
      # libcheck is not instrumented, resulting in unsuppressible false
      # positives.
      - name: Run unit tests under asan+lsan+ubsan
        env:
          ASAN_OPTIONS: abort_on_error=1,check_initialization_order=true,debug=true,detect_invalid_pointer_pairs=2,detect_leaks=1,detect_stack_use_after_return=true,strict_string_checks=true,verbosity=0
          CC: ${{ matrix.compiler }}
          CFLAGS: -fsanitize=address,undefined
          LDFLAGS: -fsanitize=address,undefined
        if: ${{ matrix.compiler == 'clang' && matrix.container == 'ubuntu:18.04' }}
        run: |
          cd proftpd
          make clean
          ./configure LIBS="-lm -lsubunit -lrt -pthread" --enable-devel --enable-redis --enable-tests --with-modules=mod_tls:mod_proxy
          make
          cd contrib/mod_proxy
          export ASAN_SYMBOLIZER_PATH=$(readlink -f $(which llvm-symbolizer-10))
          make TEST_VERBOSE=1 check

      - name: Check HTML docs
        if: ${{ matrix.container == 'ubuntu:18.04' }}
        run: |
          cd proftpd/contrib/mod_proxy
          for f in $(/bin/ls *.html); do echo "Processing $f"; tidy -errors -omit -q $f; done || exit 0
