Skip to content

SMOL 4. Binsuite

SMOL 4. Binsuite #574

Workflow file for this run

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