# Can be used locally with https://github.com/nektos/act
# Note the XXX line below.

name: BuildTest
on:
  pull_request:
  push:
    branches:
      - master
jobs:
  build:
    runs-on: ${{ matrix.os || 'ubuntu-22.04' }}
    strategy:
      matrix:
        # XXX uncomment the line below to work with act, see https://github.com/nektos/act/issues/996
        # name: []

        # Rather than a boolean False we use eg
        #   runcheck: 'no'
        # Otherwise GH expressions will make a None var
        # compare with False. We want an undefined default of True.

        # MULTI and NOWRITEV are passed as integers to the build
        include:
          - name: plain linux

          - name: multi binary
            multi: 1
            multilink: 1

          - name: multi binary, dropbearmulti argv0
            multi: 1
            multiwrapper: 1

          - name: bundled libtom, bionic , no writev()
            # test can use an older distro with bundled libtommath
            os: ubuntu-18.04
            configure_flags: --enable-bundled-libtom --enable-werror
            # NOWRITEV is unrelated, test here to save a job
            nowritev: 1
            # our tests expect >= python3.7
            runcheck: 'no'

          - name: linux clang
            cc: clang

          # Some platforms only have old compilers, we try to keep
          # compatibilty. For some reason -std=c89 doesn't enforce
          # early declarations so we specify it anyway.
          - name: c89
            extracflags: -std=c89 -Wdeclaration-after-statement

          - name: macos 12
            os: macos-12
            cc: clang
            # OS X says daemon() and utmp are deprecated.
            # OS X tests for undefined TARGET_OS_EMBEDDED in libc headers
            extracflags: -Wno-deprecated-declarations -Wno-undef
            runcheck: 'no'
            apt: 'no'
            # fails with:
            # .../ranlib: file: libtomcrypt.a(cbc_setiv.o) has no symbols
            ranlib: ranlib -no_warning_for_no_symbols

          - name: macos 11
            os: macos-11
            cc: clang
            extracflags: -Wno-deprecated-declarations -Wno-undef
            runcheck: 'no'
            apt: 'no'
            ranlib: ranlib -no_warning_for_no_symbols

          # Check that debug code doesn't bitrot
          - name: DEBUG_TRACE
            localoptions: |
              #define DEBUG_TRACE 5

          # Check off-by-default options don't bitrot
          - name: nondefault options
            nondefault: 1
            configure_flags: --enable-pam

          # # Fuzzers run standalone. A bit superfluous with cifuzz, but
          # # good to run the whole corpus to keep it working.
          # - name: fuzzing with address sanitizer
          #   configure_flags: --enable-fuzz --disable-harden --enable-bundled-libtom --enable-werror
          #   ldflags: -fsanitize=address
          #   extracflags: -fsanitize=address
          #   # -fsanitize=address prevents aslr, don't test it
          #   pytest_addopts: -k "not aslr"
          #   fuzz: True
          #   cc: clang

          # # Undefined Behaviour sanitizer
          # - name: fuzzing with undefined behaviour sanitizer
          #   configure_flags: --enable-fuzz --disable-harden --enable-bundled-libtom --enable-werror
          #   ldflags: -fsanitize=undefined
          #   # don't fail with alignment due to https://github.com/libtom/libtomcrypt/issues/549
          #   extracflags: -fsanitize=undefined -fno-sanitize-recover=undefined -fsanitize-recover=alignment
          #   pytest_addopts: -k "not aslr"
          #   fuzz: True
          #   cc: clang

    env:
      MULTI: ${{ matrix.multi }}
      CC: ${{ matrix.cc || 'gcc' }}
      LDFLAGS: ${{ matrix.ldflags }}
      EXTRACFLAGS: ${{ matrix.extracflags }}
      CONFIGURE_FLAGS: ${{ matrix.configure_flags || '--enable-werror' }}
      # for fuzzing
      CXX: clang++
      RANLIB: ${{ matrix.ranlib || 'ranlib' }}
      # pytest in "make check" recognises this for extra arguments
      PYTEST_ADDOPTS: ${{ matrix.pytest_addopts }}
      # some pytests depend on special setup from this file. see authorized_keys below.
      DBTEST_IN_ACTION: true
      LOCALOPTIONS: ${{ matrix.localoptions }}

    steps:
      - name: deps
        if: ${{ matrix.apt != 'no' }}
        run: |
          sudo apt-get -y update
          sudo apt-get -y install zlib1g-dev libtomcrypt-dev libtommath-dev mercurial python3-venv libpam0g-dev $CC

      - uses: actions/checkout@v2

      - name: configure
        run: ./configure $CONFIGURE_FLAGS CFLAGS="-O2 -Wall -Wno-pointer-sign $EXTRACFLAGS" --prefix="$HOME/inst" || (cat config.log; exit 1)

      - name: nowritev
        if: ${{ matrix.nowritev }}
        run: sed -i -e s/HAVE_WRITEV/DONT_HAVE_WRITEV/ config.h

      - name: localoptions
        run: |
          echo "$LOCALOPTIONS" > localoptions.h
          echo "#define DROPBEAR_DSS 1" >> localoptions.h
          cat localoptions.h

      - name: nondefault
        if: ${{ matrix.nondefault }}
        run: |
          # Turn on anything that's off by default. Rough but seems sufficient
          grep ' 0$' default_options.h | sed 's/0$/1/' > localoptions.h
          # PAM clashes with password
          echo "#define DROPBEAR_SVR_PASSWORD_AUTH 0" >> localoptions.h
          # 1 second timeout is too short
          sed -i "s/DEFAULT_IDLE_TIMEOUT 1/DEFAULT_IDLE_TIMEOUT 99/" localoptions.h

      - name: make
        run: make -j3

      - name: multilink
        if: ${{ matrix.multilink }}
        run: make multilink

      - name: multi wrapper script
        if: ${{ matrix.multiwrapper }}
        run: |
          cp .github/multiwrapper dropbear
          cp .github/multiwrapper dbclient
          cp .github/multiwrapper dropbearkey
          cp .github/multiwrapper dropbearconvert

      - name: makefuzz
        run: make fuzzstandalone
        if: ${{ matrix.fuzz }}

        # avoid concurrent install, osx/freebsd is racey (https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=208093)
      - name: make install
        run: make install

      - name: keys
        run: |
          mkdir -p ~/.ssh
          # remove old files so we can rerun in-place with "act -r" during test development
          rm -vf ~/.ssh/id_dropbear*
          ~/inst/bin/dropbearkey -t ecdsa -f ~/.ssh/id_dropbear | grep ^ecdsa > ~/.ssh/authorized_keys

          # to test setting SSH_PUBKEYINFO, replace the trailing comment
          ~/inst/bin/dropbearkey -t ecdsa -f ~/.ssh/id_dropbear_key2 | grep ^ecdsa | sed 's/[^ ]*$/key2 extra/' >> ~/.ssh/authorized_keys
          ~/inst/bin/dropbearkey -t ecdsa -f ~/.ssh/id_dropbear_key3 | grep ^ecdsa | sed 's/[^ ]*$/key3%char/' >> ~/.ssh/authorized_keys
          ~/inst/bin/dropbearkey -t ecdsa -f ~/.ssh/id_dropbear_key4 | grep ^ecdsa | sed 's/[^ ]*$/key4,char/' >> ~/.ssh/authorized_keys
          chmod 700 ~ ~/.ssh ~/.ssh/authorized_keys
          ls -ld ~ ~/.ssh ~/.ssh/authorized_keys

        # upload config.log if something has failed
      - name: config.log
        if: ${{ !env.ACT && (failure() || cancelled()) }}
        uses: actions/upload-artifact@v2
        with:
          name: config.log
          path: config.log

      - name: check
        if: ${{ matrix.runcheck != 'no' }}
        run: make check

      # Sanity check that the binary runs
      - name: genrsa
        run: ~/inst/bin/dropbearkey -t rsa -f testrsa
      - name: gendss
        run: ~/inst/bin/dropbearkey -t dss -f testdss
      - name: genecdsa256
        run: ~/inst/bin/dropbearkey -t ecdsa -f testec256 -s 256
      - name: genecdsa384
        run: ~/inst/bin/dropbearkey -t ecdsa -f testec384 -s 384
      - name: genecdsa521
        run: ~/inst/bin/dropbearkey -t ecdsa -f testec521 -s 521
      - name: gened25519
        run: ~/inst/bin/dropbearkey -t ed25519 -f tested25519

      - name: fuzz
        if: ${{ matrix.fuzz }}
        run: ./fuzzers_test.sh
