Build iperf3 Static Binaries #1087
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 --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 |