name: Build Artifacts (OpenWrt Binary)
on:
  workflow_dispatch:
    inputs:
      logLevel:
        description: 'Log level'
        required: true
        default: 'warning'
        type: choice
        options:
        - info
        - warning
        - debug
      tags:
        description: 'Test scenario tags'
        required: false
        type: boolean
  pull_request:
    branches: [master, develop]
    paths-ignore:
    - '**/*.md'
    - '.circleci/**'
    - '.cirrus.yml'
  push:
    branches: [master, develop]
    paths-ignore:
    - '**/*.md'
    - '.circleci/**'
    - '.cirrus.yml'
  release:
    types: [published]
  schedule:
    - cron: '0 16 * * *'
concurrency:
  group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.run_id }}
  cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
  CACHE_EPOCH: 148-2
  GOPROXY: direct
jobs:
  openwrt-binary-release:
    strategy:
      fail-fast: false
      matrix:
        include:
          - arch_name: x86_64
            arch: x86_64
            build_type: MinSizeRel
            extra_flags: ''
            openwrt: 'target=x86 subtarget=64 abi=musl'
            qemu_suffix: i386
          - arch_name: x86
            arch: i486
            build_type: MinSizeRel
            extra_flags: ''
            openwrt: 'target=x86 subtarget=generic abi=musl'
            qemu_suffix: amd64
          - arch_name: aarch64_cortex-a53
            arch: aarch64
            build_type: MinSizeRel
            extra_flags: '--arm-cpu=cortex-a53'
            openwrt: 'target=sunxi subtarget=cortexa53 abi=musl'
            qemu_suffix: aarch64
          - arch_name: aarch64_cortex-a72
            arch: aarch64
            build_type: MinSizeRel
            extra_flags: '--arm-cpu=cortex-a72'
            openwrt: 'target=mvebu subtarget=cortexa72 abi=musl'
            qemu_suffix: aarch64
          - arch_name: aarch64_generic
            arch: aarch64
            build_type: MinSizeRel
            extra_flags: ''
            openwrt: 'target=rockchip subtarget=armv8 abi=musl'
            qemu_suffix: aarch64
    runs-on: ubuntu-24.04
    permissions:
      contents: write
    env:
      BUILD_TYPE: ${{ matrix.build_type }}
      OPENWRT_FLAGS: arch=${{ matrix.arch_name }} release=23.05.3 gcc_ver=12.3.0 ${{ matrix.openwrt }}
      SDK_ROOT: ${{ github.workspace }}/openwrt-sdk-${{ matrix.arch_name }}
      SDK_HOME: ${{ github.workspace }}/openwrt-sdk-${{ matrix.arch_name }}-home
    steps:
      - uses: actions/checkout@v5
      - name: Checkout with shallow submodules
        run: |
          # unshallow must come first otherwise submodule may be get unshallowed
          git fetch --tags --unshallow
          git submodule update --init --depth 1
      - name: Patch libcxxabi for both of armel and armhf
        if: ${{ matrix.arch_name == 'armel' || matrix.arch_name == 'arm' }}
        run: |
          cd third_party/libc++abi
          patch -p1 < v8-6.7.17-fix-gcc-unwind-header.patch
      - name: Cache toolchain
        id: toolchain-cache
        uses: actions/cache@v5
        with:
          path: |
            third_party/llvm-build/Release+Asserts
          key: ${{ runner.os }}-toolchain-${{ hashFiles('CLANG_REVISION') }}-v${{ env.CACHE_EPOCH }}
      - name: Cache qemu-user
        id: qemu-user-cache
        uses: actions/cache@v5
        with:
          path: |
            qemu-user*.deb
          key: ${{ runner.os }}-qemu-10.0.8-ds-0
      - name: Cache openwrt sdkhome (sysroot)
        id: sysroot-cache
        uses: actions/cache@v5
        with:
          path: |
            openwrt-sdk-*.Linux-x86_64
            ${{ env.SDK_ROOT }}
            ${{ env.SDK_HOME }}
          key: openwrt-sysroot-${{ matrix.arch_name }}-23-05-03-v1
      - uses: actions/setup-go@v6
        with:
          go-version: '>=1.20.0'
          cache-dependency-path: |
            tools/go.sum
            third_party/boringssl/src/go.sum
      - name: Build build tool
        run: |
          cd tools
          go build
      - name: "Install dependency: prebuilt clang and clang-tidy binaries"
        if: ${{ steps.toolchain-cache.outputs.cache-hit != 'true' }}
        run: |
          ./scripts/download-clang-prebuilt-binaries.py
          rm -f third_party/llvm-build/Release+Asserts/*.tgz
      - name: "Install dependency: qemu user cache"
        if: ${{ steps.qemu-user-cache.outputs.cache-hit != 'true' }}
        run: |
          wget http://ftp.us.debian.org/debian/pool/main/q/qemu/qemu-user_10.0.8+ds-0+deb13u1+b1_amd64.deb
      - name: "Install dependency: openwrt sdkhome (sysroot)"
        if: ${{ steps.sysroot-cache.outputs.cache-hit != 'true' }}
        run: |
          eval "$OPENWRT_FLAGS"

          if [ "$subtarget" ]; then
            SDK_PATH=openwrt-sdk-$release-$target-${subtarget}_gcc-${gcc_ver}_${abi}.Linux-x86_64
          else
            subtarget='generic'
            SDK_PATH=openwrt-sdk-$release-${target}_gcc-${gcc_ver}_${abi}.Linux-x86_64
          fi

          SDK_URL=https://downloads.openwrt.org/releases/$release/targets/$target/$subtarget/$SDK_PATH.tar.xz
          curl -L -O $SDK_URL

          # full_root=$SDK_PATH/staging_dir/toolchain-*_gcc-${gcc_ver}_${abi}
          full_root=$(tar -tf $SDK_PATH.tar.xz "$SDK_PATH/staging_dir/" |grep -E "/toolchain-.*_gcc-${gcc_ver}_${abi}/$")
          #tar -C $SDK_ROOT -xf $SDK_PATH.tar.xz --strip-components 3 $full_root
          tar -xf $SDK_PATH.tar.xz
          # fix dl directory
          rm -rf $SDK_PATH/dl
          ln -sf /tmp $SDK_PATH/dl
          # fix package directory
          ln -sf $PWD/openwrt $SDK_PATH/package/yass-cli

          ln -sf $SDK_PATH ${{ env.SDK_HOME }}
          ln -sf $full_root ${{ env.SDK_ROOT }}

          rm -rf ${{ env.SDK_ROOT }}/bin ${{ env.SDK_ROOT }}/libexec
      - name: Change ubuntu mirror
        run: |
          sudo sed -i 's/azure.archive.ubuntu.com/azure.archive.ubuntu.com/g' /etc/apt/sources.list
          sudo apt-get update -qq
      - name: Populate depedencies
        run: |
          sudo apt-get update -qq
          sudo apt-get install -y pkgconf bubblewrap
          # libc6-i386 interferes with x86 build
          sudo apt remove libc6-i386

          sudo dpkg -i qemu-user_*.deb
      - name: Build Binary (CLI)
        run: |
          ./tools/build --variant cli --arch ${{ matrix.arch }} \
            --system linux --subsystem openwrt --sysroot ${{ env.SDK_ROOT }} \
            -use-static-build -nc ${{ matrix.extra_flags }}
      - name: Build Binary (DYLIB)
        run: |
          ./tools/build --variant dylib --arch ${{ matrix.arch }} \
            --system linux --subsystem openwrt --sysroot ${{ env.SDK_ROOT }} \
            -build-benchmark -build-test \
            -use-static-build -nc ${{ matrix.extra_flags }}
      - uses: actions/upload-artifact@v6
        with:
          name: openwrt-release-${{ matrix.build_type }}-${{ matrix.arch_name }}-${{ github.run_id }}-dylib
          path: |
            build-linux-openwrt-${{ matrix.arch }}/devel
            build-linux-openwrt-${{ matrix.arch }}/yass.py
            build-linux-openwrt-${{ matrix.arch }}/*.so*
            !build-linux-openwrt-${{ matrix.arch }}/*.dbg
      - uses: actions/upload-artifact@v6
        with:
          name: openwrt-release-${{ matrix.build_type }}-${{ matrix.arch_name }}-${{ github.run_id }}-cli
          path: |
            build-linux-openwrt-${{ matrix.arch }}/yass_cli
            build-linux-openwrt-${{ matrix.arch }}/*.so*
            !build-linux-openwrt-${{ matrix.arch }}/*.dbg
            !build-linux-openwrt-${{ matrix.arch }}/libYASS-C.so
      - uses: actions/upload-artifact@v6
        with:
          name: openwrt-release-${{ matrix.build_type }}-${{ matrix.arch_name }}-${{ github.run_id }}-test
          path: |
            build-linux-openwrt-${{ matrix.arch }}/yass_test
            build-linux-openwrt-${{ matrix.arch }}/*.so*
            !build-linux-openwrt-${{ matrix.arch }}/*.dbg
            !build-linux-openwrt-${{ matrix.arch }}/libYASS-C.so
      - uses: actions/upload-artifact@v6
        with:
          name: openwrt-release-${{ matrix.build_type }}-${{ matrix.arch_name }}-${{ github.run_id }}-benchmark
          path: |
            build-linux-openwrt-${{ matrix.arch }}/yass_benchmark
            build-linux-openwrt-${{ matrix.arch }}/*.so*
            !build-linux-openwrt-${{ matrix.arch }}/*.dbg
            !build-linux-openwrt-${{ matrix.arch }}/libYASS-C.so
      - uses: actions/upload-artifact@v6
        with:
          name: openwrt-release-${{ matrix.build_type }}-${{ matrix.arch_name }}-${{ github.run_id }}-dbg
          path: |
            build-linux-openwrt-${{ matrix.arch }}/*.dbg
          compression-level: 9 # maximum compression
      - name: Build IPK packages
        run: |
          pushd ${{ env.SDK_HOME }}
          make defconfig
          make package/yass-cli/{clean,compile} V=s
          popd
          find ${{ env.SDK_HOME }}/ -type f -name "yass*.ipk" -exec cp -v {} . \;
          # clean up after build
          pushd ${{ env.SDK_HOME }}
          make package/yass-cli/clean V=s
          popd
      - name: Checksum IPK packages
        run: |
          echo "md5sum "
          echo "======================================================================"
          md5sum *.ipk

          echo "sha1sum "
          echo "======================================================================"
          sha1sum *.ipk

          echo "sha256sum "
          echo "======================================================================"
          sha256sum *.ipk

          echo "sha512sum "
          echo "======================================================================"
          sha512sum *.ipk
      - uses: actions/upload-artifact@v6
        with:
          name: openwrt-release-${{ matrix.build_type }}-${{ matrix.arch_name }}-${{ github.run_id }}-ipk
          path: |
            yass*.ipk
          compression-level: 0 # no compression
      - name: Disable AppArmor Restrictions (x86 and x86_64)
        if: ${{ matrix.arch_name == 'x86' || matrix.arch_name == 'x86_64' }}
        run: |
          # see https://github.com/DevToys-app/DevToys/issues/1373#issuecomment-2985518849
          sudo apt-get install -y apparmor-profiles apparmor-profiles-extra
          sudo ln -s /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap
          sudo apparmor_parser /etc/apparmor.d/bwrap
      - name: Run tests (x86 and x86_64)
        if: ${{ matrix.arch_name == 'x86' || matrix.arch_name == 'x86_64' }}
        run: |
          rm -rf $SDK_ROOT/tmp
          mkdir -p $SDK_ROOT/etc/ssl/certs $SDK_ROOT/dev $SDK_ROOT/proc $SDK_ROOT/tmp
          touch $SDK_ROOT/etc/resolv.conf
          bwrap --die-with-parent --ro-bind $SDK_ROOT / \
            --ro-bind /etc/resolv.conf /etc/resolv.conf \
            --ro-bind /etc/ssl/certs /etc/ssl/certs \
            --proc /proc --dev /dev \
            --unshare-all --share-net \
            --bind $PWD/build-linux-openwrt-${{ matrix.arch }} /tmp \
            /tmp/yass_test
      - name: Run tests (non x86, under qemu emulation)
        if: ${{ matrix.arch_name != 'x86' && matrix.arch_name != 'x86_64' }}
        run: |
          # TMPDIR is required by leveldb unittests
          qemu-${{ matrix.qemu_suffix }} -L $SDK_ROOT \
            -E TMPDIR=$PWD/build-linux-openwrt-${{ matrix.arch }} \
            $PWD/build-linux-openwrt-${{ matrix.arch }}/yass_test \
            --no_exec_proc_tests
      - name: Upload dist tarball (including debuginfo)
        if: ${{ github.event_name == 'release' && matrix.build_type == 'MinSizeRel' }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload ${{ github.event.release.tag_name }} yass*.tgz
      - name: Upload dist ipk (including debuginfo)
        if: ${{ github.event_name == 'release' && matrix.build_type == 'MinSizeRel' }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload ${{ github.event.release.tag_name }} yass*.ipk
