sipmon 0.1.7

Passive SIP/RTP signaling & media quality monitoring (pcap/live, TUI + JSONL export)
name: Release musl binary

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-C target-feature=+crt-static"

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-musl
            host: x86_64-linux-musl
            cc: musl-gcc
            ar: ar
            ranlib: ranlib
            linker: musl-gcc
            cross_image: ""
          - target: aarch64-unknown-linux-musl
            host: aarch64-linux-musl
            cc: aarch64-linux-musl-gcc
            ar: aarch64-linux-musl-ar
            ranlib: aarch64-linux-musl-ranlib
            linker: aarch64-linux-musl-gcc
            cross_image: ghcr.io/cross-rs/aarch64-unknown-linux-musl:main
    steps:
      - uses: actions/checkout@v4

      - name: Cache cargo registry / target
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            target/${{ matrix.target }}
          key: musl-${{ runner.os }}-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }}

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: x86_64-unknown-linux-musl, aarch64-unknown-linux-musl

      - name: Install musl cross toolchain
        run: |
          set -euxo pipefail
          if [ -z "${{ matrix.cross_image }}" ]; then
            sudo apt-get update
            sudo apt-get install -y musl-tools make flex bison autoconf automake libtool linux-libc-dev
            # musl-dev ships no kernel-header symlinks on a fresh runner; point
            # the musl include dir at linux-libc-dev's UAPI headers so musl-gcc
            # can find linux/if.h, linux/if_packet.h, etc.
            sudo ln -sfn /usr/include/linux /usr/include/x86_64-linux-musl/linux
            sudo ln -sfn /usr/include/x86_64-linux-gnu/asm /usr/include/x86_64-linux-musl/asm
            sudo ln -sfn /usr/include/asm-generic /usr/include/x86_64-linux-musl/asm-generic
          else
            sudo apt-get update
            sudo apt-get install -y make flex bison autoconf automake libtool
            # Pull the cross-rs aarch64-musl toolchain from ghcr.io (GitHub's
            # CDN — fast on runners, unlike musl.cc which throttles) and overlay
            # its /usr/local onto the runner. Extracting to the same absolute
            # path keeps the gcc driver's internal libexec/sysroot paths valid.
            docker pull "${{ matrix.cross_image }}"
            cid=$(docker create "${{ matrix.cross_image }}")
            docker cp "$cid:/usr/local" /tmp/xlocal
            docker rm "$cid"
            sudo cp -a /tmp/xlocal/. /usr/local/
            ${{ matrix.cc }} --version
          fi

      - name: Build static libpcap for musl
        env:
          CC: ${{ matrix.cc }}
          AR: ${{ matrix.ar }}
          RANLIB: ${{ matrix.ranlib }}
        run: |
          set -euxo pipefail
          cd /tmp
          # Source from the libpcap GitHub repo: github.com is always reachable
          # from Actions, unlike tcpdump.org (which can be blocked by CDN).
          git clone --depth 1 --branch libpcap-1.10.5 \
            https://github.com/the-tcpdump-group/libpcap libpcap-gh
          cd libpcap-gh
          autoreconf -i
          # Cross-compiling can't run probe binaries, so force the Linux
          # PF_PACKET capture type explicitly instead of auto-detecting.
          ./configure --host=${{ matrix.host }} \
            --with-pcap=linux \
            --disable-shared --enable-static \
            --without-libnl --disable-dbus --disable-bluetooth \
            --disable-usb --without-dag --without-septel --without-snf \
            --disable-rdma
          make -j$(nproc)

      - name: Configure cargo linker
        run: |
          key="CARGO_TARGET_$(echo "${{ matrix.target }}" | tr '[:lower:]' '[:upper:]' | tr '-' '_')_LINKER"
          echo "$key=${{ matrix.linker }}" >> "$GITHUB_ENV"

      - name: Build release binary
        env:
          LIBPCAP_LIBDIR: /tmp/libpcap-gh
          LIBPCAP_VER: "1.10.5"
        run: cargo build --release --target ${{ matrix.target }}

      - name: Verify fully static
        run: |
          bin="target/${{ matrix.target }}/release/sipmon"
          file "$bin"
          case "$(file "$bin")" in
            *"static-pie linked"*|*"statically linked"*) ;;
            *) echo "ERROR: binary is not fully static"; exit 1 ;;
          esac

      - name: Package
        run: |
          cd target/${{ matrix.target }}/release
          cp sipmon sipmon-${{ matrix.target }}
          sha256sum sipmon-${{ matrix.target }} > sipmon-${{ matrix.target }}.sha256

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: sipmon-${{ matrix.target }}
          path: |
            target/${{ matrix.target }}/release/sipmon-${{ matrix.target }}
            target/${{ matrix.target }}/release/sipmon-${{ matrix.target }}.sha256
          if-no-files-found: error

  release:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          path: artifacts

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          files: artifacts/**/*