name: Build fio 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 fio release id: get-version run: | # Get the latest release tag from GitHub API LATEST_VERSION=$(curl -s https://api.github.com/repos/axboe/fio/releases/latest | jq -r '.tag_name') echo "Latest fio 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 "fio-$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 - 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-fio.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" # download, compile, and install libaio as static library cd ~ curl -L http://ftp.de.debian.org/debian/pool/main/liba/libaio/libaio_0.3.113.orig.tar.gz -o "libaio.tar.gz" tar xf libaio.tar.gz cd libaio-*/src CC=/root/\$CROSS-cross/bin/\$CROSS-gcc ENABLE_SHARED=0 make prefix=/hbb_exe install # Activate Holy Build Box exe compilation environment source /hbb_exe/activate # download and compile fio cd ~ curl -L "https://github.com/axboe/fio/archive/\$VERSION.tar.gz" -o "fio.tar.gz" tar xf fio.tar.gz cd fio-\${VERSION#fio-}* CC=/root/\$CROSS-cross/bin/\$CROSS-gcc ./configure --disable-native --build-static make # verify no external shared library links libcheck fio # copy fio binary to mounted dir cp fio "/io/fio_\$ARCH" EOF chmod +x compile-fio.sh - name: Compile fio 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-fio.sh - name: Verify binary run: | ARCH="${{ matrix.arch }}" ls -la fio_$ARCH file fio_$ARCH - name: Upload binary as artifact uses: actions/upload-artifact@v4 with: name: fio_${{ matrix.arch }} path: fio_${{ matrix.arch }} retention-days: 1 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 - name: Prepare binaries for scanning run: | mkdir -p scan-binaries find artifacts -name "fio_*" -type f -exec cp {} scan-binaries/ \; ls -la scan-binaries/ - 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/fio_*; 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 - name: Prepare release assets run: | mkdir -p release-assets # Move all binaries to release-assets directory find artifacts -name "fio_*" -type f -exec cp {} release-assets/ \; ls -la release-assets/ - name: Generate SHA256 checksums run: | cd release-assets sha256sum fio_* > fio-checksums.sha256 cat fio-checksums.sha256 - 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: fio-${{ needs.check-release.outputs.latest-version }} name: fio ${{ needs.check-release.outputs.latest-version }} Static Binaries body: | Static binaries for fio ${{ needs.check-release.outputs.latest-version }} Built using musl toolchains for maximum compatibility. **Architectures:** - `fio_x64` - x86_64 (64-bit) - `fio_x86` - i686 (32-bit) - `fio_aarch64` - ARM 64-bit - `fio_arm` - ARM 32-bit **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 fio-checksums.sha256 ``` For usage in YABS script, place these binaries in the `bin/fio/` directory. files: | release-assets/fio_* release-assets/fio-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 "## ⚠️ fio 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