name: Build iperf3 Static Binaries on: workflow_dispatch: jobs: check-release: runs-on: ubuntu-latest outputs: latest-version: ${{ steps.get-version.outputs.version }} should-build: ${{ steps.check-version.outputs.should-build }} steps: - name: Checkout repository uses: actions/checkout@v4 - name: Get latest iperf3 release id: get-version run: | # Get the latest release tag from GitHub API LATEST_VERSION=$(curl -s https://api.github.com/repos/esnet/iperf/releases/latest | jq -r '.tag_name') echo "Latest iperf3 version: $LATEST_VERSION" echo "version=$LATEST_VERSION" >> $GITHUB_OUTPUT - name: Check if version exists in releases id: check-version run: | VERSION="${{ steps.get-version.outputs.version }}" # Check if this version already exists in our releases if gh release view "iperf3-$VERSION" --repo ${{ github.repository }} >/dev/null 2>&1; then echo "Version $VERSION already exists in releases" echo "should-build=false" >> $GITHUB_OUTPUT else echo "Version $VERSION does not exist, should build" echo "should-build=true" >> $GITHUB_OUTPUT fi env: GH_TOKEN: ${{ github.token }} build: needs: check-release if: needs.check-release.outputs.should-build == 'true' runs-on: ubuntu-latest strategy: matrix: include: - arch: x64 cross: x86_64-linux-musl host: x86_64-linux-musl - arch: x86 cross: i686-linux-musl host: i686-linux-musl - arch: aarch64 cross: aarch64-linux-musl host: aarch64-linux-gnu # Note: ARM 32-bit compilation of iperf3 >3.15 has known issues # We'll still attempt it but expect potential failures - arch: arm cross: arm-linux-musleabihf host: arm-linux-gnueabihf steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Create compilation script run: | VERSION="${{ needs.check-release.outputs.latest-version }}" ARCH="${{ matrix.arch }}" CROSS="${{ matrix.cross }}" HOST="${{ matrix.host }}" # Single script for all architectures using musl cross-compilation cat > compile-iperf3.sh << EOF #!/bin/bash set -e # Activate Holy Build Box lib compilation environment source /hbb/activate set -x # remove obsolete CentOS repos cd /etc/yum.repos.d/ rm -f CentOS-Base.repo CentOS-SCLo-scl-rh.repo CentOS-SCLo-scl.repo CentOS-fasttrack.repo CentOS-x86_64-kernel.repo yum install -y yum-plugin-ovl # fix for docker overlay fs yum install -y xz # download musl cross compilation toolchain cd ~ curl -L "https://musl.cc/\$CROSS-cross.tgz" -o "\$CROSS-cross.tgz" tar xf "\$CROSS-cross.tgz" # Activate Holy Build Box exe compilation environment source /hbb_exe/activate # download and compile iperf3 cd ~ curl -L "https://github.com/esnet/iperf/archive/\$VERSION.tar.gz" -o "iperf.tar.gz" tar xf iperf.tar.gz cd iperf-* CC=/root/\$CROSS-cross/bin/\$CROSS-gcc ./configure --disable-shared --disable-profiling --build x86_64-pc-linux-gnu --host "\$HOST" --with-openssl=no --enable-static-bin make # verify no external shared library links libcheck src/iperf3 # copy iperf3 binary to mounted dir cp src/iperf3 "/io/iperf3_\$ARCH" EOF chmod +x compile-iperf3.sh - name: Compile iperf3 binary run: | ARCH="${{ matrix.arch }}" CROSS="${{ matrix.cross }}" HOST="${{ matrix.host }}" # Use musl cross-compilation for all architectures docker run -t -i --rm -v $(pwd):/io --env ARCH=$ARCH --env CROSS=$CROSS --env HOST=$HOST --env VERSION="${{ needs.check-release.outputs.latest-version }}" phusion/holy-build-box-64:latest bash /io/compile-iperf3.sh - name: Verify binary run: | ARCH="${{ matrix.arch }}" ls -la iperf3_$ARCH file iperf3_$ARCH - name: Upload binary as artifact uses: actions/upload-artifact@v4 with: name: iperf3_${{ matrix.arch }} path: iperf3_${{ matrix.arch }} retention-days: 1 # Allow arm builds to fail as ARM 32-bit has known compilation issues with newer versions continue-on-error: ${{ matrix.arch == 'arm' }} virustotal-scan: needs: [check-release, build] if: needs.check-release.outputs.should-build == 'true' runs-on: ubuntu-latest outputs: scan-results: ${{ steps.scan-summary.outputs.results }} all-clean: ${{ steps.scan-summary.outputs.all-clean }} steps: - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts continue-on-error: true - name: Prepare binaries for scanning run: | mkdir -p scan-binaries find artifacts -name "iperf3_*" -type f -exec cp {} scan-binaries/ \; || true ls -la scan-binaries/ # Check if we have at least the x64 binary if [ ! -f "scan-binaries/iperf3_x64" ]; then echo "Error: No iperf3_x64 binary found" exit 1 fi - name: Upload binaries to VirusTotal and scan id: virustotal-upload run: | echo "## VirusTotal Scan Results" > scan_results.md echo "| Binary | Status | Malicious | Suspicious | Undetected | VirusTotal URL |" >> scan_results.md echo "|--------|--------|-----------|------------|------------|----------------|" >> scan_results.md ALL_CLEAN=true SCAN_DATA="" for binary in scan-binaries/iperf3_*; do filename=$(basename "$binary") echo "Uploading $filename to VirusTotal..." # Upload to VirusTotal upload_response=$(curl -s --request POST \ --url https://www.virustotal.com/api/v3/files \ --header 'accept: application/json' \ --header 'content-type: multipart/form-data' \ --header "x-apikey: ${{ secrets.VIRUSTOTAL_API_KEY }}" \ --form "file=@$binary") analysis_id=$(echo "$upload_response" | jq -r '.data.id') echo "Analysis ID for $filename: $analysis_id" if [ "$analysis_id" = "null" ] || [ -z "$analysis_id" ]; then echo "Failed to upload $filename to VirusTotal" echo "| $filename | Upload Failed | N/A | N/A | N/A | N/A |" >> scan_results.md ALL_CLEAN=false continue fi # Store analysis ID for later retrieval echo "${filename}:${analysis_id}" >> analysis_ids.txt done echo "all_clean_upload=$ALL_CLEAN" >> $GITHUB_OUTPUT # Wait 2 minutes for scans to complete echo "Waiting 2 minutes for VirusTotal scans to complete..." sleep 120 - name: Retrieve VirusTotal scan results id: get-results run: | ALL_CLEAN=true SCAN_SUMMARY="" while IFS=':' read -r filename analysis_id; do echo "Retrieving results for $filename (ID: $analysis_id)..." # Get scan results result_response=$(curl -s --request GET \ --url "https://www.virustotal.com/api/v3/analyses/$analysis_id" \ --header 'accept: application/json' \ --header "x-apikey: ${{ secrets.VIRUSTOTAL_API_KEY }}") status=$(echo "$result_response" | jq -r '.data.attributes.status // "unknown"') if [ "$status" = "completed" ]; then malicious=$(echo "$result_response" | jq -r '.data.attributes.stats.malicious // 0') suspicious=$(echo "$result_response" | jq -r '.data.attributes.stats.suspicious // 0') undetected=$(echo "$result_response" | jq -r '.data.attributes.stats.undetected // 0') # Get file hash from the item link item_link=$(echo "$result_response" | jq -r '.data.links.item // ""') file_hash=$(echo "$item_link" | sed 's/.*files\///') vt_url="https://www.virustotal.com/gui/file/$file_hash" if [ "$malicious" -gt 0 ] || [ "$suspicious" -gt 0 ]; then status_text="⚠️ FLAGGED" ALL_CLEAN=false else status_text="✅ Clean" fi echo "| $filename | $status_text | $malicious | $suspicious | $undetected | [$file_hash]($vt_url) |" >> scan_results.md else echo "| $filename | ⏳ Pending | N/A | N/A | N/A | Scan not completed |" >> scan_results.md ALL_CLEAN=false fi done < analysis_ids.txt echo "all_clean=$ALL_CLEAN" >> $GITHUB_OUTPUT # Add summary to GitHub step summary cat scan_results.md >> $GITHUB_STEP_SUMMARY - name: Create scan summary id: scan-summary run: | RESULTS=$(cat scan_results.md) ALL_CLEAN="${{ steps.get-results.outputs.all_clean }}" # Escape newlines for GitHub output RESULTS_ESCAPED=$(echo "$RESULTS" | sed ':a;N;$!ba;s/\n/\\n/g') echo "results=$RESULTS_ESCAPED" >> $GITHUB_OUTPUT echo "all-clean=$ALL_CLEAN" >> $GITHUB_OUTPUT - name: Upload scan results as artifact uses: actions/upload-artifact@v4 with: name: virustotal-scan-results path: | scan_results.md analysis_ids.txt retention-days: 30 create-release: needs: [check-release, build, virustotal-scan] if: needs.check-release.outputs.should-build == 'true' && needs.virustotal-scan.outputs.all-clean == 'true' runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts # Continue even if some artifacts (like arm) failed continue-on-error: true - name: Prepare release assets run: | mkdir -p release-assets # Move all binaries to release-assets directory find artifacts -name "iperf3_*" -type f -exec cp {} release-assets/ \; || true ls -la release-assets/ # Check if we have at least the x64 binary if [ ! -f "release-assets/iperf3_x64" ]; then echo "Error: No iperf3_x64 binary found" exit 1 fi - name: Generate SHA256 checksums run: | cd release-assets if ls iperf3_* 1> /dev/null 2>&1; then sha256sum iperf3_* > iperf3-checksums.sha256 cat iperf3-checksums.sha256 else echo "No iperf3 binaries found to checksum" exit 1 fi - name: Check for ARM binary and add note id: check-arm run: | if [ -f "release-assets/iperf3_arm" ]; then echo "arm-note=" >> $GITHUB_OUTPUT else echo "arm-note=**Note:** ARM 32-bit binary compilation failed (known issue with iperf3 versions >3.15). Other architectures compiled successfully." >> $GITHUB_OUTPUT fi - name: Download VirusTotal scan results uses: actions/download-artifact@v4 with: name: virustotal-scan-results path: vt-results - name: Create Release uses: softprops/action-gh-release@v1 with: tag_name: iperf3-${{ needs.check-release.outputs.latest-version }} name: iperf3 ${{ needs.check-release.outputs.latest-version }} Static Binaries body: | Static binaries for iperf3 ${{ needs.check-release.outputs.latest-version }} Built using musl toolchains for maximum compatibility. **Architectures:** - `iperf3_x64` - x86_64 (64-bit) - `iperf3_x86` - i686 (32-bit) - `iperf3_aarch64` - ARM 64-bit - `iperf3_arm` - ARM 32-bit (if available) ${{ steps.check-arm.outputs.arm-note }} **Security Verification:** All binaries have been scanned by VirusTotal and verified clean. ${{ needs.virustotal-scan.outputs.scan-results }} **Checksum Verification:** ```bash # Verify checksums sha256sum -c iperf3-checksums.sha256 ``` For usage in YABS script, place these binaries in the `bin/iperf/` directory. files: | release-assets/iperf3_* release-assets/iperf3-checksums.sha256 draft: false prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} notify-failure: needs: [check-release, build, virustotal-scan] if: always() && needs.check-release.outputs.should-build == 'true' && (failure() || needs.virustotal-scan.outputs.all-clean == 'false') runs-on: ubuntu-latest steps: - name: Send failure notification run: | echo "## ⚠️ iperf3 Build Failed" >> $GITHUB_STEP_SUMMARY echo "Version: ${{ needs.check-release.outputs.latest-version }}" >> $GITHUB_STEP_SUMMARY if [ "${{ needs.virustotal-scan.outputs.all-clean }}" == "false" ]; then echo "**Reason:** VirusTotal scan detected issues with one or more binaries" >> $GITHUB_STEP_SUMMARY echo "**Scan Results:**" >> $GITHUB_STEP_SUMMARY echo "${{ needs.virustotal-scan.outputs.scan-results }}" >> $GITHUB_STEP_SUMMARY else echo "**Reason:** Build compilation failed" >> $GITHUB_STEP_SUMMARY fi echo "Check the workflow logs for details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY