name: CI

on:
  push:
    paths-ignore:
    - '.devcontainer/**'
    - 'examples/**'
    - 'lib/**'
    - 'man/**'
    - 'priv/**'
    - '**.md'
  pull_request:
    paths-ignore:
    - '.devcontainer/**'
    - 'examples/**'
    - 'lib/**'
    - 'man/**'
    - 'priv/**'
    - '**.md'

jobs:

  tests:
    name: Tests
    strategy:
      fail-fast: false
      matrix:
        otp: ['20.0', '25', '26', '27']
    runs-on: ubuntu-20.04
    services:
      redis:
        image: redis
        ports:
        - 6379:6379

    steps:

    - uses: actions/checkout@v4

    - name: Test shell scripts
      if: matrix.otp == '26'
      run: |
        shellcheck test/ejabberd_SUITE_data/gencerts.sh
        shellcheck tools/captcha.sh
        shellcheck ejabberd.init.template
        shellcheck -x ejabberdctl.template

    - name: Get specific Erlang/OTP
      uses: erlef/setup-beam@v1
      with:
        otp-version: ${{ matrix.otp }}

    - name: Get a compatible Rebar3
      if: matrix.otp < 24
      run: |
        rm rebar3
        wget https://github.com/processone/ejabberd/raw/21.12/rebar3
        chmod +x rebar3

    - name: Install MS SQL Server
      run: |
        docker run -d -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=ejabberd_Test1" \
                   -v $(pwd)/test/docker/db/mssql/initdb/initdb_mssql.sql:/initdb_mssql.sql:ro \
                   -v $(pwd)/sql/mssql.sql:/mssql.sql:ro \
                   -v $(pwd)/sql/mssql.new.sql:/mssql.new.sql:ro \
                   -p 1433:1433 --name ejabberd-mssql "mcr.microsoft.com/mssql/server:2019-latest"
        sleep 10

    - name: Prepare databases
      run: |
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql
        sudo systemctl start mysql.service
        sudo systemctl start postgresql.service
        mysql -u root -proot -e "CREATE DATABASE ejabberd_test;"
        mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost'
                                 IDENTIFIED BY 'ejabberd_test';"
        mysql -u root -proot -e "GRANT ALL ON ejabberd_test.*
                                 TO 'ejabberd_test'@'localhost';"
        pg_isready
        sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;"
        sudo -u postgres psql -c "CREATE USER ejabberd_test
                                  WITH PASSWORD 'ejabberd_test';"
        sudo -u postgres psql -c "GRANT ALL PRIVILEGES
                                  ON DATABASE ejabberd_test TO ejabberd_test;"
        sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
                                                TABLES IN SCHEMA public
                                                TO ejabberd_test;"
        sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
                                                SEQUENCES IN SCHEMA public
                                                TO ejabberd_test;"

    - name: Prepare libraries
      run: |
        sudo apt-get -qq update
        sudo apt-get -y purge libgd3 nginx
        sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \
                                 libsqlite3-dev libwebp-dev libyaml-dev

    - name: Remove syntax_tools from release
      run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script

    - name: Cache Hex.pm
      uses: actions/cache@v4
      with:
        path: |
          ~/.cache/rebar3/
        key: ${{matrix.otp}}-${{hashFiles('rebar.config')}}

    - name: Get compatible Rebar binaries
      if: matrix.otp < 21
      run: ./rebar3 unlock eredis

    - name: Download test logs
      if: matrix.otp == '26' && github.repository == 'processone/ejabberd'
      continue-on-error: true
      run: |
        mkdir -p _build/test
        curl -sSL https://github.com/processone/ecil/tarball/gh-pages |
          tar -C _build/test --strip-components=1 --wildcards -xzf -
        rm -rf _build/test/logs/last/

    - name: Compile
      run: |
        ./autogen.sh
        ./configure --with-rebar=./rebar3 \
                    --prefix=/tmp/ejabberd \
                    --enable-all \
                    --disable-elixir \
                    --disable-mssql \
                    --disable-odbc
        make

    - run: make install -s
    - run: make hooks
    - run: make options
    - run: make xref
    - run: make dialyzer
    - run: make test-eunit
    - run: make elvis
      if: matrix.otp >= 23

    - name: Check Production Release
      run: |
        make rel
        RE=_build/prod/rel/ejabberd
        $RE/bin/ejabberdctl start
        $RE/bin/ejabberdctl started
        $RE/bin/ejabberdctl stop
        $RE/bin/ejabberdctl stopped
        cat $RE/logs/ejabberd.log
        grep -q "is stopped in" $RE/logs/ejabberd.log

    - name: Start Development Release
      run: |
        make dev
        RE=_build/dev/rel/ejabberd
        sed -i 's/starttls_required: true/starttls_required: false/g'  $RE/conf/ejabberd.yml
        $RE/bin/ejabberdctl start
        $RE/bin/ejabberdctl started
        $RE/bin/ejabberdctl register admin localhost admin
        grep -q "is started in" $RE/logs/ejabberd.log

    - name: Run XMPP Interoperability Tests against CI server.
      if: matrix.otp == '26'
      continue-on-error: true
      uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.4.0
      with:
        domain: 'localhost'
        adminAccountUsername: 'admin'
        adminAccountPassword: 'admin'
        disabledSpecifications: RFC6121,XEP-0030,XEP-0045,XEP-0054,XEP-0060,XEP-0080,XEP-0115,XEP-0118,XEP-0215,XEP-0347,XEP-0363,XEP-0384

    - name: Stop Development Release
      if: always()
      run: |
        RE=_build/dev/rel/ejabberd
        $RE/bin/ejabberdctl stop
        $RE/bin/ejabberdctl stopped
        cat $RE/logs/ejabberd.log
        grep -q "is stopped in" $RE/logs/ejabberd.log

    - name: Run tests
      id: ct
      run: |
        (cd priv && ln -sf ../sql)
        sed -i -e 's/ct:pal/ct:log/' test/suite.erl
        COMMIT=`echo $GITHUB_SHA | cut -c 1-7`
        DATE=`date +%s`
        REF_NAME=`echo $GITHUB_REF_NAME | tr "/" "_"`
        NODENAME=$DATE@$GITHUB_RUN_NUMBER-$GITHUB_ACTOR-$REF_NAME-$COMMIT
        LABEL=`git show -s --format=%s | cut -c 1-30`
        ./rebar3 ct --name $NODENAME --label "$LABEL"
        ./rebar3 cover

    - name: Check results
      if: always() && (steps.ct.outcome != 'skipped' || steps.ct2.outcome != 'skipped')
      id: ctresults
      run: |
        [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
        ln `find logs/ -name suite.log` logs/suite.log
        grep 'TEST COMPLETE' logs/suite.log
        grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
        test $(find logs/ -empty -name error.log)

    - name: View logs failures
      if: failure() && steps.ctresults.outcome == 'failure'
      run: |
        cat logs/suite.log | awk \
          'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
        find logs/ -name error.log -exec cat '{}' ';'
        find logs/ -name exunit.log -exec cat '{}' ';'

    - name: Send to coveralls
      if: matrix.otp == '26'
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        DIAGNOSTIC=1 ./rebar3 as test coveralls send
        curl -v -k https://coveralls.io/webhook \
             --header "Content-Type: application/json" \
             --data '{"repo_name":"$GITHUB_REPOSITORY",
                      "repo_token":"$GITHUB_TOKEN",
                      "payload":{"build_num":$GITHUB_RUN_ID,
                                 "status":"done"}}'

    - name: Upload test logs
      if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd'
      uses: peaceiris/actions-gh-pages@v4
      with:
        publish_dir: _build/test
        exclude_assets: '.github,lib,plugins'
        external_repository: processone/ecil
        deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
        keep_files: true

    - name: View ECIL address
      if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd'
      run: |
        CTRUN=`ls -la _build/test/logs/last | sed 's|.*-> ||'`
        echo "::notice::View CT results: https://processone.github.io/ecil/logs/$CTRUN/"

    - name: Check for changes to trigger schema upgrade test
      uses: dorny/paths-filter@v3
      id: filter
      with:
        filters: |
          sql:
            - 'sql/**'
            - 'src/mod_admin_update_sql.erl'

    - name: Prepare for schema upgrade test
      id: prepupgradetest
      if: ${{ steps.filter.outputs.sql == 'true' }}
      run: |
        [[ -d logs ]] && rm -rf logs
        [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true
        sed -i 's|update_sql, false|update_sql, true|g' test/suite.erl
    - name: Run DB tests on upgraded schema (mssql, mysql, pgsql)
      run: CT_BACKENDS=mssql,mysql,pgsql make test
      if: always() && steps.prepupgradetest.outcome != 'skipped'
      id: ctupgradedschema
    - name: Check results
      if: always() && steps.ctupgradedschema.outcome != 'skipped'
      run: |
        [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
        ln `find logs/ -name suite.log` logs/suite.log
        grep 'TEST COMPLETE' logs/suite.log
        grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
        test $(find logs/ -empty -name error.log)
    - name: View logs failures
      if: failure() && steps.ctupgradedschema.outcome != 'skipped'
      run: |
        cat logs/suite.log | awk \
          'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
        find logs/ -name error.log -exec cat '{}' ';'
        find logs/ -name exunit.log -exec cat '{}' ';'

    - name: Prepare new schema
      run: |
        [[ -d logs ]] && rm -rf logs
        [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];"
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];"
        mysql -u root -proot -e "DROP DATABASE ejabberd_test;"
        sudo -u postgres psql -c "DROP DATABASE ejabberd_test;"
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql
        mysql -u root -proot -e "CREATE DATABASE ejabberd_test;"
        mysql -u root -proot -e "GRANT ALL ON ejabberd_test.*
                                 TO 'ejabberd_test'@'localhost';"
        sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;"
        sudo -u postgres psql -c "GRANT ALL PRIVILEGES
                                  ON DATABASE ejabberd_test TO ejabberd_test;"
        sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
                                                TABLES IN SCHEMA public
                                                TO ejabberd_test;"
        sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
                                                SEQUENCES IN SCHEMA public
                                                TO ejabberd_test;"
        sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl
    - name: Run DB tests on new schema (mssql, mysql, pgsql)
      run: CT_BACKENDS=mssql,mysql,pgsql make test
      id: ctnewschema
    - name: Check results
      if: always() && steps.ctnewschema.outcome != 'skipped'
      run: |
        [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
        ln `find logs/ -name suite.log` logs/suite.log
        grep 'TEST COMPLETE' logs/suite.log
        grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
        test $(find logs/ -empty -name error.log)
    - name: View logs failures
      if: failure() && steps.ctnewschema.outcome != 'skipped'
      run: |
        cat logs/suite.log | awk \
          'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
        find logs/ -name error.log -exec cat '{}' ';'
        find logs/ -name exunit.log -exec cat '{}' ';'

    - name: Upload CT logs
      if: failure()
      uses: actions/upload-artifact@v4
      with:
        name: ejabberd-ct-logs-${{matrix.otp}}
        #
        # Appending the wildcard character ("*") is a trick to make
        # "ejabberd-packages" the root directory of the uploaded ZIP file:
        #
        # https://github.com/actions/upload-artifact#upload-using-multiple-paths-and-exclusions
        #
        path: _build/test/logs
        retention-days: 14
