SMOL 4. Binsuite #574
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: SMOL 4. Binsuite | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Dry run (build only, no release)' | |
| type: boolean | |
| default: true | |
| force: | |
| description: 'Force rebuild (ignore cache)' | |
| type: boolean | |
| default: false | |
| build_mode: | |
| description: 'Build mode' | |
| type: choice | |
| options: | |
| - prod | |
| - dev | |
| default: prod | |
| tools: | |
| description: 'Tools to build (comma-separated: binpress,binflate,binject or "all")' | |
| type: string | |
| default: 'all' | |
| debug: | |
| description: 'Debug (0|1|namespace[,...])' | |
| required: false | |
| default: '0' | |
| type: string | |
| workflow_call: | |
| inputs: | |
| dry_run: | |
| type: boolean | |
| default: true | |
| force: | |
| type: boolean | |
| default: false | |
| build_mode: | |
| type: string | |
| default: prod | |
| tools: | |
| type: string | |
| default: 'all' | |
| debug: | |
| type: string | |
| default: '0' | |
| permissions: | |
| contents: read # Read repository contents | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| # Job group 1: binpress | |
| binpress: | |
| permissions: | |
| contents: read # Read repository contents | |
| id-token: write # OIDC authentication for Depot builds | |
| name: binpress / ${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.libc == 'musl' && '-musl' || '' }} | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 30 | |
| if: inputs.tools == 'all' || contains(inputs.tools, 'binpress') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # macOS builds | |
| # Use Depot runner for darwin-arm64: 8 vCPU M2, 24GB RAM | |
| - runner: depot-macos-15 | |
| platform: darwin | |
| arch: arm64 | |
| os: macos | |
| # Use Intel runner for darwin-x64 (macos-15 is ARM64) | |
| - runner: macos-15-large | |
| platform: darwin | |
| arch: x64 | |
| os: macos | |
| # Linux glibc builds | |
| - runner: ubuntu-22.04 | |
| platform: linux | |
| arch: x64 | |
| os: linux | |
| libc: glibc | |
| - runner: ubuntu-22.04-arm | |
| platform: linux | |
| arch: arm64 | |
| os: linux | |
| libc: glibc | |
| # Linux musl builds (Alpine) | |
| - runner: ubuntu-22.04 | |
| platform: linux | |
| arch: x64 | |
| os: linux | |
| libc: musl | |
| - runner: ubuntu-22.04-arm | |
| platform: linux | |
| arch: arm64 | |
| os: linux | |
| libc: musl | |
| # Windows builds | |
| - runner: windows-2022 | |
| platform: win32 | |
| arch: x64 | |
| os: windows | |
| # Windows ARM64: Cross-compile on x64 runner (no ARM64 hosted runners available) | |
| - runner: windows-2022 | |
| platform: win32 | |
| arch: arm64 | |
| os: windows | |
| cross_compile: true | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| submodules: recursive | |
| - name: Setup Node.js | |
| uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 | |
| with: | |
| node-version-file: .node-version | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup macOS code signing | |
| if: matrix.os == 'macos' | |
| run: | | |
| # Create temporary keychain for code signing. | |
| # Required for ad-hoc signing (codesign --sign -) in CI environment. | |
| security create-keychain -p actions build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p actions build.keychain | |
| security set-keychain-settings -t 3600 -u build.keychain | |
| - name: Setup Depot CLI | |
| if: matrix.os == 'linux' | |
| uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0 | |
| with: | |
| oidc: true | |
| - name: Check if tool should be built | |
| id: build-enabled | |
| shell: bash | |
| env: | |
| TOOLS_INPUT: ${{ inputs.tools || 'all' }} | |
| run: | | |
| TOOL="binpress" | |
| if [ "$TOOLS_INPUT" = "all" ]; then | |
| echo "build=true" >> $GITHUB_OUTPUT | |
| echo "√ Building $TOOL (all tools selected)" | |
| elif echo ",$TOOLS_INPUT," | grep -q ",$TOOL,"; then | |
| echo "build=true" >> $GITHUB_OUTPUT | |
| echo "√ Building $TOOL (explicitly selected)" | |
| else | |
| echo "build=false" >> $GITHUB_OUTPUT | |
| echo "⏭ Skipping $TOOL (not selected)" | |
| fi | |
| - name: Setup checkpoint caching | |
| if: steps.build-enabled.outputs.build == 'true' | |
| id: setup-checkpoints | |
| uses: ./.github/actions/setup-checkpoints | |
| with: | |
| package-name: binpress | |
| build-mode: ${{ inputs.build_mode || 'prod' }} | |
| platform: ${{ matrix.platform }} | |
| arch: ${{ matrix.arch }} | |
| libc: ${{ matrix.libc || '' }} | |
| - name: Validate checkpoint binary exists | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'false' | |
| id: validate-checkpoint | |
| shell: bash | |
| working-directory: packages/binpress | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| run: | | |
| BINARY="build/${BUILD_MODE}/out/Final/binpress" | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="${BINARY}.exe" | |
| fi | |
| if [ ! -f "$BINARY" ]; then | |
| echo "⚠️ Checkpoint restored but binary missing at: $BINARY" | |
| echo "force-rebuild=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "✅ Binary exists: $BINARY" | |
| echo "force-rebuild=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Install QEMU and Docker (Linux - for cross-compiled binary testing) | |
| if: matrix.os == 'linux' && matrix.runner == 'ubuntu-22.04' && (matrix.arch == 'arm64' || matrix.libc == 'musl') | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| sudo apt-get update | |
| # Install QEMU for ARM64 emulation (if ARM64 build) | |
| if [ "${ARCH}" = "arm64" ]; then | |
| echo "Installing QEMU user-mode for ARM64 emulation..." | |
| sudo apt-get install -y qemu-user-static | |
| echo "√ QEMU installed" | |
| fi | |
| # Docker is pre-installed on GitHub Actions runners (for musl testing) | |
| if [ "${LIBC}" = "musl" ]; then | |
| echo "Verifying Docker is available for musl testing..." | |
| docker --version || echo "⚠ Docker not available" | |
| fi | |
| - name: Setup compiler (Linux) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc g++ make | |
| - name: Install dependencies (Linux glibc) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' && matrix.platform == 'linux' | |
| run: | | |
| # Install OpenSSL for Linux builds | |
| sudo apt-get install -y libssl-dev | |
| - name: Install musl toolchain (Linux musl) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' && matrix.libc == 'musl' | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| run: | | |
| # Install musl cross-compilation toolchain | |
| sudo apt-get install -y musl-tools | |
| # For ARM64, we need the cross-compiler | |
| if [ "${ARCH}" = "arm64" ]; then | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| fi | |
| - name: Setup Windows build tools (x64) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'windows' && !matrix.cross_compile | |
| shell: bash | |
| run: | | |
| # Install MinGW for gcc/g++ (consistent ABI with LIEF library) | |
| # Use retry logic to handle transient GitHub download issues | |
| RETRIES=5 | |
| DELAY=10 | |
| SUCCESS=false | |
| for i in $(seq 1 $RETRIES); do | |
| echo "Attempt $i/$RETRIES: Installing MinGW..." | |
| if choco install -y mingw --no-progress; then | |
| echo "√ MinGW installed successfully" | |
| SUCCESS=true | |
| break | |
| fi | |
| if [ $i -lt $RETRIES ]; then | |
| echo "⚠ Attempt $i failed, retrying in ${DELAY}s..." | |
| sleep $DELAY | |
| DELAY=$((DELAY * 2)) # Exponential backoff | |
| fi | |
| done | |
| if [ "$SUCCESS" = false ]; then | |
| echo "✗ Failed to install MinGW after $RETRIES attempts" | |
| exit 1 | |
| fi | |
| gcc --version | |
| g++ --version | |
| # Ensure make is available | |
| where make || choco install -y make | |
| - name: Setup Windows build tools (ARM64 cross-compile) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'windows' && matrix.cross_compile | |
| shell: bash | |
| run: | | |
| # Download and extract llvm-mingw for ARM64 cross-compilation. | |
| curl -fsSL -o llvm-mingw.zip "https://github.com/mstorsjo/llvm-mingw/releases/download/20250528/llvm-mingw-20250528-ucrt-x86_64.zip" | |
| 7z x llvm-mingw.zip -o"C:/llvm-mingw" | |
| echo "C:/llvm-mingw/llvm-mingw-20250528-ucrt-x86_64/bin" >> $GITHUB_PATH | |
| # Ensure make is available | |
| where make || choco install -y make | |
| - name: Select Xcode version (macOS) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'macos' | |
| run: | | |
| # Select appropriate Xcode version for each runner | |
| # depot-macos-15 (ARM64 M2): Use Xcode 16.4 (default on macOS 15) | |
| # macos-15 (Intel x64): Use Xcode 16.4 (default on macOS 15) | |
| # Both macOS 15 runners use Xcode 16.4 by default, but explicitly set for clarity | |
| sudo xcode-select -s /Applications/Xcode_16.4.app | |
| xcodebuild -version | |
| clang --version | |
| - name: Debug build decision | |
| if: steps.build-enabled.outputs.build == 'true' | |
| shell: bash | |
| env: | |
| BUILD_ENABLED: ${{ steps.build-enabled.outputs.build }} | |
| BUILD_REQUIRED: ${{ steps.setup-checkpoints.outputs.build-required }} | |
| CACHE_HIT: ${{ steps.setup-checkpoints.outputs.cache-hit }} | |
| CACHE_VALID: ${{ steps.setup-checkpoints.outputs.cache-valid }} | |
| run: | | |
| echo "🔍 Build decision variables:" | |
| echo " build-enabled: ${BUILD_ENABLED}" | |
| echo " build-required: ${BUILD_REQUIRED}" | |
| echo " cache-hit: ${CACHE_HIT}" | |
| echo " cache-valid: ${CACHE_VALID}" | |
| echo "" | |
| if [ "${BUILD_REQUIRED}" = "true" ]; then | |
| echo "✅ Build will run" | |
| else | |
| echo "⏭️ Build will be skipped (checkpoint restored)" | |
| fi | |
| - name: Load cache version (binpress) | |
| id: cache-version | |
| shell: bash | |
| run: | | |
| echo "version=$(jq -r '.versions["binpress"]' .github/cache-versions.json)" >> $GITHUB_OUTPUT | |
| echo "lief_version=$(jq -r '.versions["lief"]' .github/cache-versions.json)" >> $GITHUB_OUTPUT | |
| echo "stubs_version=$(jq -r '.versions["stubs"]' .github/cache-versions.json)" >> $GITHUB_OUTPUT | |
| - name: Initialize lzfse submodule before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| run: | | |
| git submodule update --init packages/bin-infra/upstream/lzfse | |
| - name: Initialize libdeflate submodule before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| run: | | |
| git submodule update --init packages/binject/upstream/libdeflate | |
| - name: Get lzfse submodule SHA (binpress) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| id: lzfse-sha | |
| shell: bash | |
| run: | | |
| LZFSE_SHA=$(git -C packages/bin-infra/upstream/lzfse rev-parse --short=7 HEAD) | |
| echo "sha=${LZFSE_SHA}" >> $GITHUB_OUTPUT | |
| echo "lzfse submodule SHA: ${LZFSE_SHA}" | |
| - name: Clean LIEF before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| run: | | |
| rm -rf packages/bin-infra/build/${BUILD_MODE}/out/Final/lief/ | |
| - name: Build binpress with Depot (Linux - offloads compute) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1.16.2 | |
| with: | |
| project: 8fpj9495vw | |
| platforms: linux/${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }} | |
| build-args: | | |
| BUILD_MODE=${{ steps.setup-checkpoints.outputs.build-mode }} | |
| GH_TOKEN=${{ secrets.GITHUB_TOKEN }} | |
| CACHE_VERSION=${{ steps.cache-version.outputs.version }} | |
| LIEF_CACHE_VERSION=${{ steps.cache-version.outputs.lief_version }} | |
| LZFSE_CACHE_VERSION=${{ steps.lzfse-sha.outputs.sha }} | |
| STUBS_CACHE_VERSION=${{ steps.cache-version.outputs.stubs_version }} | |
| file: packages/binpress/Dockerfile.${{ matrix.libc == 'musl' && 'musl' || 'glibc' }} | |
| target: export | |
| outputs: type=local,dest=.docker-export | |
| context: . | |
| - name: Move Docker export to correct locations (binpress) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| run: | | |
| echo "Moving Docker export to correct locations..." | |
| mv .docker-export/binpress-build packages/binpress/build | |
| mkdir -p packages/build-infra/build | |
| mv .docker-export/build-infra-downloaded packages/build-infra/build/downloaded | |
| rm -rf .docker-export | |
| echo "✅ Docker export moved to correct locations" | |
| - name: Verify Depot build output (binpress) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| working-directory: packages/binpress | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| run: | | |
| echo "Verifying Depot build output..." | |
| BINARY="build/${BUILD_MODE}/out/Final/binpress" | |
| if [ ! -f "$BINARY" ]; then | |
| echo "× Binary not found at $BINARY" | |
| echo "Contents of build directory:" | |
| find build/${BUILD_MODE}/ -type f 2>/dev/null || find build/ -type f | |
| exit 1 | |
| fi | |
| echo "✅ Depot build output verified" | |
| file "$BINARY" | |
| - name: Validate Depot checkpoints (binpress) | |
| if: | | |
| matrix.os == 'linux' && | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' | |
| uses: ./.github/actions/validate-depot-checkpoints | |
| with: | |
| package-path: packages/binpress | |
| build-mode: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| package-name: binpress | |
| - name: Build binpress native (macOS/Windows) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| (steps.setup-checkpoints.outputs.build-required == 'true' || steps.validate-checkpoint.outputs.force-rebuild == 'true') && | |
| matrix.os != 'linux' | |
| shell: bash | |
| working-directory: packages/binpress | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PLATFORM: ${{ matrix.platform }} | |
| ARCH: ${{ matrix.arch }} | |
| TARGET_ARCH: ${{ matrix.arch }} | |
| DEBUG: ${{ inputs.debug && '1' || '' }} | |
| run: | | |
| echo "🔨 Building binpress for ${PLATFORM}-${ARCH} (${BUILD_MODE} mode)" | |
| # Use build script with --force to bypass checkpoint check | |
| # (checkpoint restoration is handled by setup-checkpoints action) | |
| pnpm run build -- --force | |
| echo "✅ Build complete" | |
| ls -lh build/${BUILD_MODE}/out/ | |
| - name: Verify binary | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'false' | |
| shell: bash | |
| working-directory: packages/binpress | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| run: | | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="build/${BUILD_MODE}/out/Final/binpress.exe" | |
| else | |
| BINARY="build/${BUILD_MODE}/out/Final/binpress" | |
| fi | |
| if [ ! -f "$BINARY" ]; then | |
| echo "× Binary not found: $BINARY" | |
| exit 1 | |
| fi | |
| echo "✅ Binary exists: $BINARY" | |
| # Show binary info | |
| if [ "${OS_TYPE}" = "macos" ]; then | |
| file "$BINARY" | |
| otool -L "$BINARY" || true | |
| elif [ "${OS_TYPE}" = "linux" ]; then | |
| file "$BINARY" | |
| ldd "$BINARY" || true | |
| elif [ "${OS_TYPE}" = "windows" ]; then | |
| file "$BINARY" || true | |
| fi | |
| - name: Debug compression test | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.arch == 'x64' && matrix.libc != 'musl' && runner.os == 'Windows' | |
| shell: bash | |
| working-directory: packages/binpress | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| run: | | |
| # Run debug compression test to diagnose issues | |
| echo "Running debug compression test..." | |
| bash test/debug-compress.sh || true | |
| - name: Test binary (functional test) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.arch == 'x64' && matrix.libc != 'musl' | |
| shell: bash | |
| working-directory: packages/binpress | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Run full functional tests on native x64 platforms | |
| echo "Running functional tests for binpress..." | |
| pnpm run test | |
| - name: Smoke test binary | |
| if: steps.build-enabled.outputs.build == 'true' | |
| shell: bash | |
| working-directory: packages/binpress | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| # Deterministic smoke test: native execution, Docker, QEMU, or static verification | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="build/${BUILD_MODE}/out/Final/binpress.exe" | |
| else | |
| BINARY="build/${BUILD_MODE}/out/Final/binpress" | |
| fi | |
| # Prepare smoke test options | |
| SMOKE_TEST_ARGS="$BINARY" | |
| if [ "${ARCH}" = "arm64" ]; then | |
| SMOKE_TEST_ARGS="$SMOKE_TEST_ARGS --arch arm64" | |
| fi | |
| if [ "${LIBC}" = "musl" ]; then | |
| SMOKE_TEST_ARGS="$SMOKE_TEST_ARGS --musl" | |
| fi | |
| echo "Running smoke test: node ../../packages/build-infra/scripts/smoke-test-binary.mjs $SMOKE_TEST_ARGS" | |
| node ../../packages/build-infra/scripts/smoke-test-binary.mjs $SMOKE_TEST_ARGS | |
| - name: Set artifact name | |
| shell: bash | |
| env: | |
| PLATFORM: ${{ matrix.platform }} | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| # Construct artifact name (musl suffix after arch). | |
| ARTIFACT_NAME="binpress-${PLATFORM}-${ARCH}" | |
| if [ "${LIBC}" = "musl" ]; then | |
| ARTIFACT_NAME="${ARTIFACT_NAME}-musl" | |
| fi | |
| echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV | |
| - name: Upload artifacts | |
| if: steps.build-enabled.outputs.build == 'true' | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: ${{ env.ARTIFACT_NAME }} | |
| path: packages/binpress/build/${{ steps.setup-checkpoints.outputs.build-mode }}/out/Final/binpress${{ matrix.os == 'windows' && '.exe' || '' }} | |
| retention-days: 30 | |
| if-no-files-found: error | |
| # Job group 2: binflate | |
| binflate: | |
| permissions: | |
| contents: read # Read repository contents | |
| id-token: write # OIDC authentication for Depot builds | |
| name: binflate / ${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.libc == 'musl' && '-musl' || '' }} | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 30 | |
| if: inputs.tools == 'all' || contains(inputs.tools, 'binflate') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # macOS builds | |
| # Use Depot runner for darwin-arm64: 8 vCPU M2, 24GB RAM | |
| - runner: depot-macos-15 | |
| platform: darwin | |
| arch: arm64 | |
| os: macos | |
| # Use Intel runner for darwin-x64 (macos-15 is ARM64) | |
| - runner: macos-15-large | |
| platform: darwin | |
| arch: x64 | |
| os: macos | |
| # Linux glibc builds | |
| - runner: ubuntu-22.04 | |
| platform: linux | |
| arch: x64 | |
| os: linux | |
| libc: glibc | |
| - runner: ubuntu-22.04-arm | |
| platform: linux | |
| arch: arm64 | |
| os: linux | |
| libc: glibc | |
| # Linux musl builds (Alpine) | |
| - runner: ubuntu-22.04 | |
| platform: linux | |
| arch: x64 | |
| os: linux | |
| libc: musl | |
| - runner: ubuntu-22.04-arm | |
| platform: linux | |
| arch: arm64 | |
| os: linux | |
| libc: musl | |
| # Windows builds | |
| - runner: windows-2022 | |
| platform: win32 | |
| arch: x64 | |
| os: windows | |
| # Windows ARM64: Cross-compile on x64 runner (no ARM64 hosted runners available) | |
| - runner: windows-2022 | |
| platform: win32 | |
| arch: arm64 | |
| os: windows | |
| cross_compile: true | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| submodules: recursive | |
| - name: Setup Node.js | |
| uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 | |
| with: | |
| node-version-file: .node-version | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup macOS code signing | |
| if: matrix.os == 'macos' | |
| run: | | |
| # Create temporary keychain for code signing. | |
| # Required for ad-hoc signing (codesign --sign -) in CI environment. | |
| security create-keychain -p actions build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p actions build.keychain | |
| security set-keychain-settings -t 3600 -u build.keychain | |
| - name: Setup Depot CLI | |
| if: matrix.os == 'linux' | |
| uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0 | |
| with: | |
| oidc: true | |
| - name: Install QEMU and Docker (Linux - for cross-compiled binary testing) | |
| if: matrix.os == 'linux' && matrix.runner == 'ubuntu-22.04' && (matrix.arch == 'arm64' || matrix.libc == 'musl') | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| sudo apt-get update | |
| # Install QEMU for ARM64 emulation (if ARM64 build) | |
| if [ "${ARCH}" = "arm64" ]; then | |
| echo "Installing QEMU user-mode for ARM64 emulation..." | |
| sudo apt-get install -y qemu-user-static | |
| echo "√ QEMU installed" | |
| fi | |
| # Docker is pre-installed on GitHub Actions runners (for musl testing) | |
| if [ "${LIBC}" = "musl" ]; then | |
| echo "Verifying Docker is available for musl testing..." | |
| docker --version || echo "⚠ Docker not available" | |
| fi | |
| - name: Check if tool should be built | |
| id: build-enabled | |
| shell: bash | |
| env: | |
| TOOLS_INPUT: ${{ inputs.tools || 'all' }} | |
| run: | | |
| TOOL="binflate" | |
| if [ "$TOOLS_INPUT" = "all" ]; then | |
| echo "build=true" >> $GITHUB_OUTPUT | |
| echo "√ Building $TOOL (all tools selected)" | |
| elif echo ",$TOOLS_INPUT," | grep -q ",$TOOL,"; then | |
| echo "build=true" >> $GITHUB_OUTPUT | |
| echo "√ Building $TOOL (explicitly selected)" | |
| else | |
| echo "build=false" >> $GITHUB_OUTPUT | |
| echo "⏭ Skipping $TOOL (not selected)" | |
| fi | |
| - name: Setup checkpoint caching | |
| if: steps.build-enabled.outputs.build == 'true' | |
| id: setup-checkpoints | |
| uses: ./.github/actions/setup-checkpoints | |
| with: | |
| package-name: binflate | |
| build-mode: ${{ inputs.build_mode || 'prod' }} | |
| platform: ${{ matrix.platform }} | |
| arch: ${{ matrix.arch }} | |
| libc: ${{ matrix.libc || '' }} | |
| - name: Validate checkpoint binary exists | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'false' | |
| id: validate-checkpoint | |
| shell: bash | |
| working-directory: packages/binflate | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| run: | | |
| BINARY="build/${BUILD_MODE}/out/Final/binflate" | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="${BINARY}.exe" | |
| fi | |
| if [ ! -f "$BINARY" ]; then | |
| echo "⚠️ Checkpoint restored but binary missing at: $BINARY" | |
| echo "force-rebuild=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "✅ Binary exists: $BINARY" | |
| echo "force-rebuild=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Setup compiler (Linux) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc g++ make | |
| - name: Install dependencies (Linux glibc) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' && matrix.platform == 'linux' | |
| run: | | |
| # Install OpenSSL for Linux builds | |
| sudo apt-get install -y libssl-dev | |
| - name: Install musl toolchain (Linux musl) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' && matrix.libc == 'musl' | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| run: | | |
| # Install musl cross-compilation toolchain | |
| sudo apt-get install -y musl-tools | |
| # For ARM64, we need the cross-compiler | |
| if [ "${ARCH}" = "arm64" ]; then | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| fi | |
| - name: Setup Windows build tools (x64) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'windows' && !matrix.cross_compile | |
| shell: bash | |
| run: | | |
| # Install MinGW for gcc/g++ (consistent ABI with LIEF library) | |
| # Use retry logic to handle transient GitHub download issues | |
| RETRIES=5 | |
| DELAY=10 | |
| SUCCESS=false | |
| for i in $(seq 1 $RETRIES); do | |
| echo "Attempt $i/$RETRIES: Installing MinGW..." | |
| if choco install -y mingw --no-progress; then | |
| echo "√ MinGW installed successfully" | |
| SUCCESS=true | |
| break | |
| fi | |
| if [ $i -lt $RETRIES ]; then | |
| echo "⚠ Attempt $i failed, retrying in ${DELAY}s..." | |
| sleep $DELAY | |
| DELAY=$((DELAY * 2)) # Exponential backoff | |
| fi | |
| done | |
| if [ "$SUCCESS" = false ]; then | |
| echo "✗ Failed to install MinGW after $RETRIES attempts" | |
| exit 1 | |
| fi | |
| gcc --version | |
| g++ --version | |
| # Ensure make is available | |
| where make || choco install -y make | |
| - name: Setup Windows build tools (ARM64 cross-compile) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'windows' && matrix.cross_compile | |
| shell: bash | |
| run: | | |
| # Download and extract llvm-mingw for ARM64 cross-compilation. | |
| curl -fsSL -o llvm-mingw.zip "https://github.com/mstorsjo/llvm-mingw/releases/download/20250528/llvm-mingw-20250528-ucrt-x86_64.zip" | |
| 7z x llvm-mingw.zip -o"C:/llvm-mingw" | |
| echo "C:/llvm-mingw/llvm-mingw-20250528-ucrt-x86_64/bin" >> $GITHUB_PATH | |
| # Ensure make is available | |
| where make || choco install -y make | |
| - name: Select Xcode version (macOS) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'macos' | |
| run: | | |
| # Select appropriate Xcode version for each runner | |
| # depot-macos-15 (ARM64 M2): Use Xcode 16.4 (default on macOS 15) | |
| # macos-15 (Intel x64): Use Xcode 16.4 (default on macOS 15) | |
| # Both macOS 15 runners use Xcode 16.4 by default, but explicitly set for clarity | |
| sudo xcode-select -s /Applications/Xcode_16.4.app | |
| xcodebuild -version | |
| clang --version | |
| - name: Debug build decision | |
| if: steps.build-enabled.outputs.build == 'true' | |
| shell: bash | |
| env: | |
| BUILD_ENABLED: ${{ steps.build-enabled.outputs.build }} | |
| BUILD_REQUIRED: ${{ steps.setup-checkpoints.outputs.build-required }} | |
| CACHE_HIT: ${{ steps.setup-checkpoints.outputs.cache-hit }} | |
| CACHE_VALID: ${{ steps.setup-checkpoints.outputs.cache-valid }} | |
| run: | | |
| echo "🔍 Build decision variables:" | |
| echo " build-enabled: ${BUILD_ENABLED}" | |
| echo " build-required: ${BUILD_REQUIRED}" | |
| echo " cache-hit: ${CACHE_HIT}" | |
| echo " cache-valid: ${CACHE_VALID}" | |
| echo "" | |
| if [ "${BUILD_REQUIRED}" = "true" ]; then | |
| echo "✅ Build will run" | |
| else | |
| echo "⏭️ Build will be skipped (checkpoint restored)" | |
| fi | |
| - name: Load cache version (binflate) | |
| id: cache-version | |
| shell: bash | |
| run: | | |
| echo "version=$(jq -r '.versions["binflate"]' .github/cache-versions.json)" >> $GITHUB_OUTPUT | |
| - name: Initialize lzfse submodule before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| run: | | |
| git submodule update --init packages/bin-infra/upstream/lzfse | |
| - name: Initialize libdeflate submodule before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| run: | | |
| git submodule update --init packages/binject/upstream/libdeflate | |
| - name: Get lzfse submodule SHA (binflate) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| id: lzfse-sha | |
| shell: bash | |
| run: | | |
| LZFSE_SHA=$(git -C packages/bin-infra/upstream/lzfse rev-parse --short=7 HEAD) | |
| echo "sha=${LZFSE_SHA}" >> $GITHUB_OUTPUT | |
| echo "lzfse submodule SHA: ${LZFSE_SHA}" | |
| - name: Build binflate with Depot (Linux - offloads compute) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1.16.2 | |
| with: | |
| project: 8fpj9495vw | |
| platforms: linux/${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }} | |
| build-args: | | |
| BUILD_MODE=${{ steps.setup-checkpoints.outputs.build-mode }} | |
| CACHE_VERSION=${{ steps.cache-version.outputs.version }} | |
| LZFSE_CACHE_VERSION=${{ steps.lzfse-sha.outputs.sha }} | |
| file: packages/binflate/Dockerfile.${{ matrix.libc == 'musl' && 'musl' || 'glibc' }} | |
| target: export | |
| outputs: type=local,dest=packages/binflate/build | |
| context: . | |
| - name: Verify Depot build output (binflate) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| working-directory: packages/binflate | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| run: | | |
| echo "Verifying Depot build output..." | |
| BINARY="build/${BUILD_MODE}/out/Final/binflate" | |
| if [ ! -f "$BINARY" ]; then | |
| echo "× Binary not found at $BINARY" | |
| echo "Contents of build directory:" | |
| find build/${BUILD_MODE}/ -type f 2>/dev/null || find build/ -type f | |
| exit 1 | |
| fi | |
| echo "✅ Depot build output verified" | |
| file "$BINARY" | |
| - name: Validate Depot checkpoints (binflate) | |
| if: | | |
| matrix.os == 'linux' && | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' | |
| uses: ./.github/actions/validate-depot-checkpoints | |
| with: | |
| package-path: packages/binflate | |
| build-mode: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| package-name: binflate | |
| - name: Build binflate native (macOS/Windows) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| (steps.setup-checkpoints.outputs.build-required == 'true' || steps.validate-checkpoint.outputs.force-rebuild == 'true') && | |
| matrix.os != 'linux' | |
| shell: bash | |
| working-directory: packages/binflate | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PLATFORM: ${{ matrix.platform }} | |
| ARCH: ${{ matrix.arch }} | |
| TARGET_ARCH: ${{ matrix.arch }} | |
| DEBUG: ${{ inputs.debug && '1' || '' }} | |
| run: | | |
| echo "🔨 Building binflate for ${PLATFORM}-${ARCH} (${BUILD_MODE} mode)" | |
| # Use build script with --force to bypass checkpoint check | |
| # (checkpoint restoration is handled by setup-checkpoints action) | |
| pnpm run build -- --force | |
| echo "✅ Build complete" | |
| ls -lh build/${BUILD_MODE}/out/ | |
| - name: Verify binary | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'false' | |
| shell: bash | |
| working-directory: packages/binflate | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| run: | | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="build/${BUILD_MODE}/out/Final/binflate.exe" | |
| else | |
| BINARY="build/${BUILD_MODE}/out/Final/binflate" | |
| fi | |
| if [ ! -f "$BINARY" ]; then | |
| echo "× Binary not found: $BINARY" | |
| exit 1 | |
| fi | |
| echo "✅ Binary exists: $BINARY" | |
| # Show binary info | |
| if [ "${OS_TYPE}" = "macos" ]; then | |
| file "$BINARY" | |
| otool -L "$BINARY" || true | |
| elif [ "${OS_TYPE}" = "linux" ]; then | |
| file "$BINARY" | |
| ldd "$BINARY" || true | |
| elif [ "${OS_TYPE}" = "windows" ]; then | |
| file "$BINARY" || true | |
| fi | |
| - name: Test binary (functional test) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.arch == 'x64' && matrix.libc != 'musl' | |
| shell: bash | |
| working-directory: packages/binflate | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Run full functional tests on native x64 platforms | |
| echo "Running functional tests for binflate..." | |
| pnpm run test | |
| - name: Smoke test binary | |
| if: steps.build-enabled.outputs.build == 'true' | |
| shell: bash | |
| working-directory: packages/binflate | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| # Deterministic smoke test: native execution, Docker, QEMU, or static verification | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="build/${BUILD_MODE}/out/Final/binflate.exe" | |
| else | |
| BINARY="build/${BUILD_MODE}/out/Final/binflate" | |
| fi | |
| # Prepare smoke test options | |
| SMOKE_TEST_ARGS="$BINARY" | |
| if [ "${ARCH}" = "arm64" ]; then | |
| SMOKE_TEST_ARGS="$SMOKE_TEST_ARGS --arch arm64" | |
| fi | |
| if [ "${LIBC}" = "musl" ]; then | |
| SMOKE_TEST_ARGS="$SMOKE_TEST_ARGS --musl" | |
| fi | |
| echo "Running smoke test: node ../../packages/build-infra/scripts/smoke-test-binary.mjs $SMOKE_TEST_ARGS" | |
| node ../../packages/build-infra/scripts/smoke-test-binary.mjs $SMOKE_TEST_ARGS | |
| - name: Set artifact name | |
| shell: bash | |
| env: | |
| PLATFORM: ${{ matrix.platform }} | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| # Construct artifact name (musl suffix after arch). | |
| ARTIFACT_NAME="binflate-${PLATFORM}-${ARCH}" | |
| if [ "${LIBC}" = "musl" ]; then | |
| ARTIFACT_NAME="${ARTIFACT_NAME}-musl" | |
| fi | |
| echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV | |
| - name: Upload artifacts | |
| if: steps.build-enabled.outputs.build == 'true' | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: ${{ env.ARTIFACT_NAME }} | |
| path: packages/binflate/build/${{ steps.setup-checkpoints.outputs.build-mode }}/out/Final/binflate${{ matrix.os == 'windows' && '.exe' || '' }} | |
| retention-days: 30 | |
| if-no-files-found: error | |
| # Job group 3: binject | |
| binject: | |
| permissions: | |
| contents: read # Read repository contents | |
| id-token: write # OIDC authentication for Depot builds | |
| name: binject / ${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.libc == 'musl' && '-musl' || '' }} | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 30 | |
| if: inputs.tools == 'all' || contains(inputs.tools, 'binject') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # macOS builds | |
| # Use Depot runner for darwin-arm64: 8 vCPU M2, 24GB RAM | |
| - runner: depot-macos-15 | |
| platform: darwin | |
| arch: arm64 | |
| os: macos | |
| # Use Intel runner for darwin-x64 (macos-15 is ARM64) | |
| - runner: macos-15-large | |
| platform: darwin | |
| arch: x64 | |
| os: macos | |
| # Linux glibc builds | |
| - runner: ubuntu-22.04 | |
| platform: linux | |
| arch: x64 | |
| os: linux | |
| libc: glibc | |
| - runner: ubuntu-22.04-arm | |
| platform: linux | |
| arch: arm64 | |
| os: linux | |
| libc: glibc | |
| # Linux musl builds (Alpine) | |
| - runner: ubuntu-22.04 | |
| platform: linux | |
| arch: x64 | |
| os: linux | |
| libc: musl | |
| - runner: ubuntu-22.04-arm | |
| platform: linux | |
| arch: arm64 | |
| os: linux | |
| libc: musl | |
| # Windows builds | |
| - runner: windows-2022 | |
| platform: win32 | |
| arch: x64 | |
| os: windows | |
| # Windows ARM64: Cross-compile on x64 runner (no ARM64 hosted runners available) | |
| - runner: windows-2022 | |
| platform: win32 | |
| arch: arm64 | |
| os: windows | |
| cross_compile: true | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| submodules: recursive | |
| - name: Setup Node.js | |
| uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 | |
| with: | |
| node-version-file: .node-version | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup macOS code signing | |
| if: matrix.os == 'macos' | |
| run: | | |
| # Create temporary keychain for code signing. | |
| # Required for ad-hoc signing (codesign --sign -) in CI environment. | |
| security create-keychain -p actions build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p actions build.keychain | |
| security set-keychain-settings -t 3600 -u build.keychain | |
| - name: Setup Depot CLI | |
| if: matrix.os == 'linux' | |
| uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0 | |
| with: | |
| oidc: true | |
| - name: Install QEMU and Docker (Linux - for cross-compiled binary testing) | |
| if: matrix.os == 'linux' && matrix.runner == 'ubuntu-22.04' && (matrix.arch == 'arm64' || matrix.libc == 'musl') | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| sudo apt-get update | |
| # Install QEMU for ARM64 emulation (if ARM64 build) | |
| if [ "${ARCH}" = "arm64" ]; then | |
| echo "Installing QEMU user-mode for ARM64 emulation..." | |
| sudo apt-get install -y qemu-user-static | |
| echo "√ QEMU installed" | |
| fi | |
| # Docker is pre-installed on GitHub Actions runners (for musl testing) | |
| if [ "${LIBC}" = "musl" ]; then | |
| echo "Verifying Docker is available for musl testing..." | |
| docker --version || echo "⚠ Docker not available" | |
| fi | |
| - name: Check if tool should be built | |
| id: build-enabled | |
| shell: bash | |
| env: | |
| TOOLS_INPUT: ${{ inputs.tools || 'all' }} | |
| run: | | |
| TOOL="binject" | |
| if [ "$TOOLS_INPUT" = "all" ]; then | |
| echo "build=true" >> $GITHUB_OUTPUT | |
| echo "√ Building $TOOL (all tools selected)" | |
| elif echo ",$TOOLS_INPUT," | grep -q ",$TOOL,"; then | |
| echo "build=true" >> $GITHUB_OUTPUT | |
| echo "√ Building $TOOL (explicitly selected)" | |
| else | |
| echo "build=false" >> $GITHUB_OUTPUT | |
| echo "⏭ Skipping $TOOL (not selected)" | |
| fi | |
| - name: Setup checkpoint caching | |
| if: steps.build-enabled.outputs.build == 'true' | |
| id: setup-checkpoints | |
| uses: ./.github/actions/setup-checkpoints | |
| with: | |
| package-name: binject | |
| build-mode: ${{ inputs.build_mode || 'prod' }} | |
| platform: ${{ matrix.platform }} | |
| arch: ${{ matrix.arch }} | |
| libc: ${{ matrix.libc || '' }} | |
| - name: Validate checkpoint binary exists | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'false' | |
| id: validate-checkpoint | |
| shell: bash | |
| working-directory: packages/binject | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| run: | | |
| BINARY="build/${BUILD_MODE}/out/Final/binject" | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="${BINARY}.exe" | |
| fi | |
| if [ ! -f "$BINARY" ]; then | |
| echo "⚠️ Checkpoint restored but binary missing at: $BINARY" | |
| echo "force-rebuild=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "✅ Binary exists: $BINARY" | |
| echo "force-rebuild=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Setup compiler (Linux) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc g++ make | |
| - name: Install dependencies (Linux glibc) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' && matrix.platform == 'linux' | |
| run: | | |
| # Install OpenSSL for Linux builds | |
| sudo apt-get install -y libssl-dev | |
| - name: Install musl toolchain (Linux musl) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'linux' && matrix.libc == 'musl' | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| run: | | |
| # Install musl cross-compilation toolchain | |
| sudo apt-get install -y musl-tools | |
| # For ARM64, we need the cross-compiler | |
| if [ "${ARCH}" = "arm64" ]; then | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| fi | |
| - name: Setup Windows build tools (x64) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'windows' && !matrix.cross_compile | |
| shell: bash | |
| run: | | |
| # Install MinGW for gcc/g++ (consistent ABI with LIEF library) | |
| # Use retry logic to handle transient GitHub download issues | |
| RETRIES=5 | |
| DELAY=10 | |
| SUCCESS=false | |
| for i in $(seq 1 $RETRIES); do | |
| echo "Attempt $i/$RETRIES: Installing MinGW..." | |
| if choco install -y mingw --no-progress; then | |
| echo "√ MinGW installed successfully" | |
| SUCCESS=true | |
| break | |
| fi | |
| if [ $i -lt $RETRIES ]; then | |
| echo "⚠ Attempt $i failed, retrying in ${DELAY}s..." | |
| sleep $DELAY | |
| DELAY=$((DELAY * 2)) # Exponential backoff | |
| fi | |
| done | |
| if [ "$SUCCESS" = false ]; then | |
| echo "✗ Failed to install MinGW after $RETRIES attempts" | |
| exit 1 | |
| fi | |
| gcc --version | |
| g++ --version | |
| # Ensure make is available | |
| where make || choco install -y make | |
| - name: Setup Windows build tools (ARM64 cross-compile) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'windows' && matrix.cross_compile | |
| shell: bash | |
| run: | | |
| # Download and extract llvm-mingw for ARM64 cross-compilation. | |
| curl -fsSL -o llvm-mingw.zip "https://github.com/mstorsjo/llvm-mingw/releases/download/20250528/llvm-mingw-20250528-ucrt-x86_64.zip" | |
| 7z x llvm-mingw.zip -o"C:/llvm-mingw" | |
| echo "C:/llvm-mingw/llvm-mingw-20250528-ucrt-x86_64/bin" >> $GITHUB_PATH | |
| # Ensure make is available | |
| where make || choco install -y make | |
| - name: Select Xcode version (macOS) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'macos' | |
| run: | | |
| # Select appropriate Xcode version for each runner | |
| # depot-macos-15 (ARM64 M2): Use Xcode 16.4 (default on macOS 15) | |
| # macos-15 (Intel x64): Use Xcode 16.4 (default on macOS 15) | |
| # Both macOS 15 runners use Xcode 16.4 by default, but explicitly set for clarity | |
| sudo xcode-select -s /Applications/Xcode_16.4.app | |
| xcodebuild -version | |
| clang --version | |
| - name: Install CMake (Windows) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.os == 'windows' | |
| shell: bash | |
| run: | | |
| echo "Installing CMake for LIEF build..." | |
| choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y | |
| echo "Refreshing PATH to include CMake..." | |
| export PATH="/c/Program Files/CMake/bin:$PATH" | |
| echo "Verifying CMake installation..." | |
| cmake --version | |
| - name: Debug build decision | |
| if: steps.build-enabled.outputs.build == 'true' | |
| shell: bash | |
| env: | |
| BUILD_ENABLED: ${{ steps.build-enabled.outputs.build }} | |
| BUILD_REQUIRED: ${{ steps.setup-checkpoints.outputs.build-required }} | |
| CACHE_HIT: ${{ steps.setup-checkpoints.outputs.cache-hit }} | |
| CACHE_VALID: ${{ steps.setup-checkpoints.outputs.cache-valid }} | |
| run: | | |
| echo "🔍 Build decision variables:" | |
| echo " build-enabled: ${BUILD_ENABLED}" | |
| echo " build-required: ${BUILD_REQUIRED}" | |
| echo " cache-hit: ${CACHE_HIT}" | |
| echo " cache-valid: ${CACHE_VALID}" | |
| echo "" | |
| if [ "${BUILD_REQUIRED}" = "true" ]; then | |
| echo "✅ Build will run" | |
| else | |
| echo "⏭️ Build will be skipped (checkpoint restored)" | |
| fi | |
| - name: Load cache version (binject) | |
| id: cache-version | |
| shell: bash | |
| run: | | |
| echo "version=$(jq -r '.versions["binject"]' .github/cache-versions.json)" >> $GITHUB_OUTPUT | |
| echo "lief_version=$(jq -r '.versions["lief"]' .github/cache-versions.json)" >> $GITHUB_OUTPUT | |
| echo "stubs_version=$(jq -r '.versions["stubs"]' .github/cache-versions.json)" >> $GITHUB_OUTPUT | |
| - name: Initialize lzfse submodule before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| run: | | |
| git submodule update --init packages/bin-infra/upstream/lzfse | |
| - name: Initialize libdeflate submodule before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| run: | | |
| git submodule update --init packages/binject/upstream/libdeflate | |
| - name: Initialize cJSON submodule before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| run: | | |
| git submodule update --init packages/binject/upstream/cJSON | |
| - name: Get submodule SHAs for cache (binject) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| id: submodule-sha | |
| shell: bash | |
| run: | | |
| LIBDEFLATE_SHA=$(git -C packages/binject/upstream/libdeflate rev-parse --short=7 HEAD) | |
| CJSON_SHA=$(git -C packages/binject/upstream/cJSON rev-parse --short=7 HEAD) | |
| LZFSE_SHA=$(git -C packages/bin-infra/upstream/lzfse rev-parse --short=7 HEAD) | |
| echo "libdeflate=${LIBDEFLATE_SHA}" >> $GITHUB_OUTPUT | |
| echo "cjson=${CJSON_SHA}" >> $GITHUB_OUTPUT | |
| echo "lzfse=${LZFSE_SHA}" >> $GITHUB_OUTPUT | |
| echo "Submodule SHAs: libdeflate=${LIBDEFLATE_SHA}, cJSON=${CJSON_SHA}, lzfse=${LZFSE_SHA}" | |
| - name: Clean LIEF before Docker build (Linux) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| shell: bash | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| run: | | |
| echo "🧹 Cleaning LIEF directory before Docker build to ensure fresh download inside container..." | |
| rm -rf packages/bin-infra/build/${BUILD_MODE}/out/Final/lief/ | |
| echo "✅ LIEF directory cleaned" | |
| - name: Build binject with Depot (Linux - offloads compute) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' && | |
| matrix.os == 'linux' | |
| uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1.16.2 | |
| with: | |
| project: 8fpj9495vw | |
| platforms: linux/${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }} | |
| build-args: | | |
| BUILD_MODE=${{ steps.setup-checkpoints.outputs.build-mode }} | |
| GH_TOKEN=${{ secrets.GITHUB_TOKEN }} | |
| CACHE_VERSION=${{ steps.cache-version.outputs.version }} | |
| LIEF_CACHE_VERSION=${{ steps.cache-version.outputs.lief_version }} | |
| LIBDEFLATE_CACHE_VERSION=${{ steps.submodule-sha.outputs.libdeflate }} | |
| CJSON_CACHE_VERSION=${{ steps.submodule-sha.outputs.cjson }} | |
| LZFSE_CACHE_VERSION=${{ steps.submodule-sha.outputs.lzfse }} | |
| STUBS_CACHE_VERSION=${{ steps.cache-version.outputs.stubs_version }} | |
| file: packages/binject/Dockerfile.${{ matrix.libc == 'musl' && 'musl' || 'glibc' }} | |
| target: export | |
| outputs: type=local,dest=packages/binject/build | |
| context: . | |
| - name: Verify Depot build output (binject) | |
| if: | | |
| matrix.os == 'linux' && | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' | |
| shell: bash | |
| working-directory: packages/binject | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| run: | | |
| echo "Verifying Depot build output..." | |
| BINARY="build/${BUILD_MODE}/out/Final/binject" | |
| if [ ! -f "$BINARY" ]; then | |
| echo "× Binary not found at $BINARY" | |
| echo "Contents of build directory:" | |
| find build/${BUILD_MODE}/ -type f 2>/dev/null || find build/ -type f | |
| exit 1 | |
| fi | |
| echo "✅ Depot build output verified" | |
| file "$BINARY" | |
| - name: Validate Depot checkpoints (binject) | |
| if: | | |
| matrix.os == 'linux' && | |
| steps.build-enabled.outputs.build == 'true' && | |
| steps.setup-checkpoints.outputs.build-required == 'true' | |
| uses: ./.github/actions/validate-depot-checkpoints | |
| with: | |
| package-path: packages/binject | |
| build-mode: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| package-name: binject | |
| - name: Build binject native (macOS/Windows) | |
| if: | | |
| steps.build-enabled.outputs.build == 'true' && | |
| (steps.setup-checkpoints.outputs.build-required == 'true' || steps.validate-checkpoint.outputs.force-rebuild == 'true') && | |
| matrix.os != 'linux' | |
| shell: bash | |
| working-directory: packages/binject | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PLATFORM: ${{ matrix.platform }} | |
| ARCH: ${{ matrix.arch }} | |
| TARGET_ARCH: ${{ matrix.arch }} | |
| DEBUG: ${{ inputs.debug && '1' || '' }} | |
| run: | | |
| echo "🔨 Building binject for ${PLATFORM}-${ARCH} (${BUILD_MODE} mode)" | |
| # Use build script with --force to bypass checkpoint check. | |
| # (checkpoint restoration is handled by setup-checkpoints action). | |
| # Build script handles LIEF (macOS), platform detection, and Makefile selection. | |
| # TARGET_ARCH env var enables ARM64 cross-compilation with llvm-mingw. | |
| pnpm run build -- --force | |
| echo "✅ Build complete" | |
| ls -lh build/${BUILD_MODE}/out/ | |
| - name: Verify binary | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'false' | |
| shell: bash | |
| working-directory: packages/binject | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| run: | | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="build/${BUILD_MODE}/out/Final/binject.exe" | |
| else | |
| BINARY="build/${BUILD_MODE}/out/Final/binject" | |
| fi | |
| if [ ! -f "$BINARY" ]; then | |
| echo "× Binary not found: $BINARY" | |
| exit 1 | |
| fi | |
| echo "✅ Binary exists: $BINARY" | |
| # Show binary info | |
| if [ "${OS_TYPE}" = "macos" ]; then | |
| file "$BINARY" | |
| otool -L "$BINARY" || true | |
| elif [ "${OS_TYPE}" = "linux" ]; then | |
| file "$BINARY" | |
| ldd "$BINARY" || true | |
| elif [ "${OS_TYPE}" = "windows" ]; then | |
| file "$BINARY" || true | |
| fi | |
| - name: Test binary (functional test) | |
| if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true' && matrix.arch == 'x64' && matrix.libc != 'musl' | |
| shell: bash | |
| working-directory: packages/binject | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Run full functional tests on native x64 platforms | |
| echo "Running functional tests for binject..." | |
| node scripts/test.mjs | |
| - name: Smoke test binary | |
| if: steps.build-enabled.outputs.build == 'true' | |
| shell: bash | |
| working-directory: packages/binject | |
| env: | |
| BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }} | |
| OS_TYPE: ${{ matrix.os }} | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| # Deterministic smoke test: native execution, Docker, QEMU, or static verification | |
| if [ "${OS_TYPE}" = "windows" ]; then | |
| BINARY="build/${BUILD_MODE}/out/Final/binject.exe" | |
| else | |
| BINARY="build/${BUILD_MODE}/out/Final/binject" | |
| fi | |
| # Prepare smoke test options | |
| SMOKE_TEST_ARGS="$BINARY" | |
| if [ "${ARCH}" = "arm64" ]; then | |
| SMOKE_TEST_ARGS="$SMOKE_TEST_ARGS --arch arm64" | |
| fi | |
| if [ "${LIBC}" = "musl" ]; then | |
| SMOKE_TEST_ARGS="$SMOKE_TEST_ARGS --musl" | |
| fi | |
| echo "Running smoke test: node ../../packages/build-infra/scripts/smoke-test-binary.mjs $SMOKE_TEST_ARGS" | |
| node ../../packages/build-infra/scripts/smoke-test-binary.mjs $SMOKE_TEST_ARGS | |
| - name: Set artifact name | |
| shell: bash | |
| env: | |
| PLATFORM: ${{ matrix.platform }} | |
| ARCH: ${{ matrix.arch }} | |
| LIBC: ${{ matrix.libc }} | |
| run: | | |
| # Construct artifact name (musl suffix after arch). | |
| ARTIFACT_NAME="binject-${PLATFORM}-${ARCH}" | |
| if [ "${LIBC}" = "musl" ]; then | |
| ARTIFACT_NAME="${ARTIFACT_NAME}-musl" | |
| fi | |
| echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV | |
| - name: Upload artifacts | |
| if: steps.build-enabled.outputs.build == 'true' | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: ${{ env.ARTIFACT_NAME }} | |
| path: packages/binject/build/${{ steps.setup-checkpoints.outputs.build-mode }}/out/Final/binject${{ matrix.os == 'windows' && '.exe' || '' }} | |
| retention-days: 30 | |
| if-no-files-found: error | |
| release: | |
| name: Release ${{ matrix.tool }} | |
| needs: [binpress, binflate, binject] | |
| if: github.event_name == 'workflow_dispatch' && !inputs.dry_run | |
| runs-on: ubuntu-22.04 | |
| permissions: | |
| contents: write # Required to create GitHub releases | |
| strategy: | |
| matrix: | |
| tool: [binpress, binflate, binject] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: | | |
| .github/scripts/generate-version.sh | |
| sparse-checkout-cone-mode: false | |
| - name: Check if tool should be released | |
| id: should-release | |
| shell: bash | |
| env: | |
| TOOLS_INPUT: ${{ inputs.tools || 'all' }} | |
| TOOL: ${{ matrix.tool }} | |
| run: | | |
| if [ "$TOOLS_INPUT" = "all" ]; then | |
| echo "release=true" >> $GITHUB_OUTPUT | |
| echo "√ Releasing $TOOL (all tools selected)" | |
| elif echo "$TOOLS_INPUT" | grep -q "$TOOL"; then | |
| echo "release=true" >> $GITHUB_OUTPUT | |
| echo "√ Releasing $TOOL (explicitly selected)" | |
| else | |
| echo "release=false" >> $GITHUB_OUTPUT | |
| echo "⏭ Skipping $TOOL release (not selected)" | |
| fi | |
| - name: Download all artifacts for ${{ matrix.tool }} | |
| if: steps.should-release.outputs.release == 'true' | |
| uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| path: artifacts/ | |
| pattern: ${{ matrix.tool }}-* | |
| - name: Organize release assets | |
| if: steps.should-release.outputs.release == 'true' | |
| env: | |
| TOOL: ${{ matrix.tool }} | |
| run: | | |
| mkdir -p dist/${TOOL} | |
| # Darwin ARM64 | |
| if [ -d "artifacts/${TOOL}-darwin-arm64" ]; then | |
| mv artifacts/${TOOL}-darwin-arm64/${TOOL} \ | |
| dist/${TOOL}/${TOOL}-darwin-arm64 2>/dev/null || \ | |
| mv artifacts/${TOOL}-darwin-arm64/${TOOL}.exe \ | |
| dist/${TOOL}/${TOOL}-darwin-arm64.exe 2>/dev/null || true | |
| fi | |
| # Darwin x64 | |
| if [ -d "artifacts/${TOOL}-darwin-x64" ]; then | |
| mv artifacts/${TOOL}-darwin-x64/${TOOL} \ | |
| dist/${TOOL}/${TOOL}-darwin-x64 2>/dev/null || \ | |
| mv artifacts/${TOOL}-darwin-x64/${TOOL}.exe \ | |
| dist/${TOOL}/${TOOL}-darwin-x64.exe 2>/dev/null || true | |
| fi | |
| # Linux glibc x64 | |
| if [ -d "artifacts/${TOOL}-linux-x64" ]; then | |
| mv artifacts/${TOOL}-linux-x64/${TOOL} \ | |
| dist/${TOOL}/${TOOL}-linux-x64 2>/dev/null || \ | |
| mv artifacts/${TOOL}-linux-x64/${TOOL}.exe \ | |
| dist/${TOOL}/${TOOL}-linux-x64.exe 2>/dev/null || true | |
| fi | |
| # Linux glibc ARM64 | |
| if [ -d "artifacts/${TOOL}-linux-arm64" ]; then | |
| mv artifacts/${TOOL}-linux-arm64/${TOOL} \ | |
| dist/${TOOL}/${TOOL}-linux-arm64 2>/dev/null || \ | |
| mv artifacts/${TOOL}-linux-arm64/${TOOL}.exe \ | |
| dist/${TOOL}/${TOOL}-linux-arm64.exe 2>/dev/null || true | |
| fi | |
| # Linux musl x64 | |
| if [ -d "artifacts/${TOOL}-linux-x64-musl" ]; then | |
| mv artifacts/${TOOL}-linux-x64-musl/${TOOL} \ | |
| dist/${TOOL}/${TOOL}-linux-x64-musl 2>/dev/null || \ | |
| mv artifacts/${TOOL}-linux-x64-musl/${TOOL}.exe \ | |
| dist/${TOOL}/${TOOL}-linux-x64-musl.exe 2>/dev/null || true | |
| fi | |
| # Linux musl ARM64 | |
| if [ -d "artifacts/${TOOL}-linux-arm64-musl" ]; then | |
| mv artifacts/${TOOL}-linux-arm64-musl/${TOOL} \ | |
| dist/${TOOL}/${TOOL}-linux-arm64-musl 2>/dev/null || \ | |
| mv artifacts/${TOOL}-linux-arm64-musl/${TOOL}.exe \ | |
| dist/${TOOL}/${TOOL}-linux-arm64-musl.exe 2>/dev/null || true | |
| fi | |
| # Windows x64 | |
| if [ -d "artifacts/${TOOL}-win32-x64" ]; then | |
| mv artifacts/${TOOL}-win32-x64/${TOOL}.exe \ | |
| dist/${TOOL}/${TOOL}-win-x64.exe 2>/dev/null || \ | |
| mv artifacts/${TOOL}-win32-x64/${TOOL} \ | |
| dist/${TOOL}/${TOOL}-win-x64 2>/dev/null || true | |
| fi | |
| # Windows ARM64 | |
| if [ -d "artifacts/${TOOL}-win32-arm64" ]; then | |
| mv artifacts/${TOOL}-win32-arm64/${TOOL}.exe \ | |
| dist/${TOOL}/${TOOL}-win-arm64.exe 2>/dev/null || \ | |
| mv artifacts/${TOOL}-win32-arm64/${TOOL} \ | |
| dist/${TOOL}/${TOOL}-win-arm64 2>/dev/null || true | |
| fi | |
| # Make Unix binaries executable | |
| chmod +x dist/${TOOL}/${TOOL}-* 2>/dev/null || true | |
| ls -lh dist/${TOOL}/ | |
| - name: Generate version | |
| if: steps.should-release.outputs.release == 'true' | |
| id: version | |
| run: | | |
| source .github/scripts/generate-version.sh | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Version: $VERSION" | |
| - name: Generate checksums | |
| if: steps.should-release.outputs.release == 'true' | |
| shell: bash | |
| env: | |
| TOOL: ${{ matrix.tool }} | |
| run: | | |
| cd dist/${TOOL} | |
| if command -v shasum &> /dev/null; then | |
| shasum -a 256 ${TOOL}-* > checksums.txt | |
| elif command -v sha256sum &> /dev/null; then | |
| sha256sum ${TOOL}-* > checksums.txt | |
| else | |
| echo "Error: No SHA-256 command found" | |
| exit 1 | |
| fi | |
| # Validate checksums.txt is not empty | |
| if [ ! -s checksums.txt ]; then | |
| echo "Error: checksums.txt is empty" | |
| exit 1 | |
| fi | |
| cat checksums.txt | |
| - name: Create GitHub Release | |
| if: steps.should-release.outputs.release == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TOOL: ${{ matrix.tool }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| RELEASE_NAME="${TOOL}" | |
| TAG="${RELEASE_NAME}-${VERSION}" | |
| # Tool descriptions | |
| case "$RELEASE_NAME" in | |
| binpress) | |
| DESCRIPTION="Binary compression tool for Mach-O, ELF, and PE executables." | |
| ;; | |
| binflate) | |
| DESCRIPTION="Binary decompression tool for extracting compressed executables." | |
| ;; | |
| binject) | |
| DESCRIPTION="Binary resource injection tool for Mach-O, ELF, and PE executables." | |
| ;; | |
| esac | |
| # Check if release already exists | |
| if gh release view "$TAG" &>/dev/null; then | |
| echo "Release $TAG already exists, uploading assets..." | |
| gh release upload "$TAG" \ | |
| dist/${TOOL}/${TOOL}-* \ | |
| dist/${TOOL}/checksums.txt \ | |
| --clobber | |
| else | |
| echo "Creating new release $TAG..." | |
| gh release create "$TAG" \ | |
| --title "${TOOL} ${VERSION}" \ | |
| --notes "${DESCRIPTION} | |
| ## Platforms | |
| - darwin-arm64 | |
| - darwin-x64 | |
| - linux-arm64 | |
| - linux-x64 | |
| - linux-arm64-musl | |
| - linux-x64-musl | |
| - win-arm64 | |
| - win-x64 | |
| ## Files | |
| - \`${TOOL}-darwin-arm64\` - macOS Apple Silicon | |
| - \`${TOOL}-darwin-x64\` - macOS Intel | |
| - \`${TOOL}-linux-arm64\` - Linux ARM64 (glibc) | |
| - \`${TOOL}-linux-x64\` - Linux x64 (glibc) | |
| - \`${TOOL}-linux-arm64-musl\` - Linux ARM64 (musl/Alpine) | |
| - \`${TOOL}-linux-x64-musl\` - Linux x64 (musl/Alpine) | |
| - \`${TOOL}-win-arm64.exe\` - Windows ARM64 | |
| - \`${TOOL}-win-x64.exe\` - Windows x64 | |
| - \`checksums.txt\` - SHA256 checksums | |
| ## Usage | |
| Download the appropriate binary for your platform and run it: | |
| \`\`\`bash | |
| ./${TOOL}-darwin-arm64 --help | |
| \`\`\`" \ | |
| dist/${TOOL}/${TOOL}-* \ | |
| dist/${TOOL}/checksums.txt | |
| fi |