Skip to content

SMOL 4. Binsuite

SMOL 4. Binsuite #719

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
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
# Job group 1: binpress
binpress:
permissions:
contents: read
id-token: write # OIDC authentication for Depot builds
name: binpress / ${{ matrix.platform == 'win32' && 'win' || 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 both darwin architectures: 8 vCPU M2, 24GB RAM
# Cross-compile x64 from ARM64 since Depot only offers ARM64 macOS
- runner: depot-macos-15
platform: darwin
arch: arm64
os: macos
- runner: depot-macos-15
platform: darwin
arch: x64
os: macos
cross_compile: true
# Linux glibc builds
- runner: ubuntu-24.04
platform: linux
arch: x64
os: linux
libc: glibc
- runner: ubuntu-24.04-arm
platform: linux
arch: arm64
os: linux
libc: glibc
# Linux musl builds (Alpine)
- runner: ubuntu-24.04
platform: linux
arch: x64
os: linux
libc: musl
- runner: ubuntu-24.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:
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@ea1986b8019fedee5fb38b485690b13ad8e0217f # main
- 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 "[OK] Building $TOOL (all tools selected)"
elif echo ",$TOOLS_INPUT," | grep -q ",$TOOL,"; then
echo "build=true" >> $GITHUB_OUTPUT
echo "[OK] 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 || '' }}
# Only create a signing keychain / install Depot CLI when a build is
# actually about to run. On cache-hit rounds the tarball already
# contains the signed binary, so spinning up an ephemeral keychain
# or fetching the Depot CLI just burns CI time.
- name: Setup macOS code signing
if: matrix.os == 'macos' && steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: ./.github/actions/setup-macos-keychain
- name: Setup Depot CLI
if: matrix.os == 'linux' && steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
with:
oidc: true
- name: Set platform-arch (binpress)
id: platform-arch
uses: ./.github/actions/set-platform-arch
with:
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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# All platforms (Linux Depot / macOS / Windows native) export to
# build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/. A prior version
# of this step had a separate Linux branch without PLATFORM_ARCH
# based on a misread of the Dockerfile — the binary would never
# be found on Linux cache-hit and force-rebuild kept firing,
# defeating the entire checkpoint cache for this matrix.
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/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: Setup build environment
if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: ./.github/actions/setup-build-env
with:
os: ${{ matrix.os }}
arch: ${{ matrix.arch }}
libc: ${{ matrix.libc || '' }}
cross-compile: ${{ matrix.cross_compile || 'false' }}
- name: Debug build decision
if: steps.build-enabled.outputs.build == 'true'
shell: bash
env:
BUILD_REQUIRED: ${{ steps.setup-checkpoints.outputs.build-required }}
CACHE_HIT: ${{ steps.setup-checkpoints.outputs.cache-hit }}
run: |
if [ "${BUILD_REQUIRED}" = "true" ]; then
echo "✅ Build will run (cache-hit: ${CACHE_HIT})"
else
echo "⏭️ Skipping build (checkpoint restored)"
fi
- name: Load cache version (binpress)
id: cache-version
shell: bash
run: |
# jq -r emits the literal string "null" on missing key with
# exit 0 — without a guard, that string would silently poison
# downstream Depot build-args (LIEF_CACHE_VERSION=null) and
# collide every matrix arm + every branch into one shared
# stale cache entry. Fail fast instead.
load_version() {
local key="$1"
local val
val=$(jq -r ".versions[\"$key\"]" .github/cache-versions.json)
if [ -z "$val" ] || [ "$val" = "null" ]; then
echo "error: cache-versions.json missing key: $key" >&2
exit 1
fi
echo "$val"
}
echo "version=$(load_version binpress)" >> $GITHUB_OUTPUT
echo "lief_version=$(load_version lief)" >> $GITHUB_OUTPUT
echo "stubs_version=$(load_version stubs)" >> $GITHUB_OUTPUT
- name: Initialize submodules before Docker build (Linux)
if: |
steps.build-enabled.outputs.build == 'true' &&
steps.setup-checkpoints.outputs.build-required == 'true' &&
matrix.os == 'linux'
uses: ./.github/actions/init-submodules
with:
# binpress links zstd (via bin-infra/make/zstd.mk) but does
# NOT use libdeflate — that's binject-only. Previously inited
# both, wasting ~10-30s of CI time per Linux matrix entry.
submodules: packages/bin-infra/upstream/zstd
- 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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
run: rm -rf packages/lief-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/lief/
- name: Stage registry tool-checksums for Docker build context
if: |
steps.build-enabled.outputs.build == 'true' &&
steps.setup-checkpoints.outputs.build-required == 'true' &&
matrix.os == 'linux'
shell: bash
run: |
# Socket-registry's setup action exports SOCKET_TOOL_CHECKSUMS_FILE
# pointing at a stable on-runner copy of external-tools.json.
# Dockerfiles `COPY .build-context/registry-tools.json …` to
# resolve pnpm version + per-platform asset + sha256 at build
# time without duplicating per-Dockerfile constants.
mkdir -p .build-context
cp "$SOCKET_TOOL_CHECKSUMS_FILE" .build-context/registry-tools.json
- 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@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1.17.0
with:
project: 8fpj9495vw
platforms: linux/${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }}
build-args: |
BUILD_MODE=${{ steps.setup-checkpoints.outputs.build-mode }}
PLATFORM_ARCH=${{ steps.platform-arch.outputs.platform_arch }}
CACHE_VERSION=${{ steps.cache-version.outputs.version }}
LIEF_CACHE_VERSION=${{ steps.cache-version.outputs.lief_version }}
STUBS_CACHE_VERSION=${{ steps.cache-version.outputs.stubs_version }}
# GH_TOKEN via BuildKit secret, not build-args. Passing it as
# ARG+ENV bakes the token into image-layer metadata visible
# through `docker history` and persisted across Depot cache
# tenants.
secrets: |
gh_token=${{ github.token }}
no-cache: ${{ inputs.force == true }}
file: packages/binpress/docker/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: |
mv .docker-export/binpress-build packages/binpress/build
mkdir -p packages/lief-builder/build
mv .docker-export/lief-builder-downloaded packages/lief-builder/build/downloaded
rm -rf .docker-export
- 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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
run: |
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binpress"
[ -f "$BINARY" ] || { echo "× Binary not found: $BINARY"; exit 1; }
echo "✅ $(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
platform-arch: ${{ steps.platform-arch.outputs.platform_arch }}
- 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: ${{ github.token }}
PLATFORM: ${{ matrix.platform }}
ARCH: ${{ matrix.arch }}
TARGET_ARCH: ${{ matrix.arch }}
DEBUG: ${{ inputs.debug != '0' && inputs.debug || '' }}
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"
# Construct PLATFORM_ARCH for build output path.
if [ "${PLATFORM}" = "win32" ]; then
RELEASE_PLATFORM="win"
else
RELEASE_PLATFORM="${PLATFORM}"
fi
PLATFORM_ARCH="${RELEASE_PLATFORM}-${ARCH}"
ls -lh build/${BUILD_MODE}/${PLATFORM_ARCH}/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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# All platforms (Linux Depot / macOS / Windows native) export to
# build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/. Matches the
# validate-checkpoint step above.
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binpress"
if [ "${OS_TYPE}" = "windows" ]; then
BINARY="${BINARY}.exe"
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' && matrix.os != 'linux'
shell: bash
working-directory: packages/binpress
env:
GH_TOKEN: ${{ github.token }}
TARGET_ARCH: ${{ matrix.arch }}
run: |
# Run full functional tests on native x64 platforms (non-Linux).
# Linux glibc tests run inside Docker to avoid LTO bytecode incompatibility.
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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
ARCH: ${{ matrix.arch }}
LIBC: ${{ matrix.libc }}
run: |
# Deterministic smoke test: native execution, Docker, QEMU, or static verification
# All platforms now use PLATFORM_ARCH in build output path.
if [ "${OS_TYPE}" = "windows" ]; then
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binpress.exe"
else
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/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.mts $SMOKE_TEST_ARGS"
node ../../packages/build-infra/scripts/smoke-test-binary.mts $SMOKE_TEST_ARGS
- name: Set artifact name and path
shell: bash
env:
PLATFORM: ${{ matrix.platform }}
ARCH: ${{ matrix.arch }}
LIBC: ${{ matrix.libc }}
BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# Use platform-arch output (win32→win conversion already done).
ARTIFACT_NAME="binpress-${PLATFORM_ARCH}"
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
# Construct binary path.
# All platforms now use PLATFORM_ARCH in build output path.
if [ "${OS_TYPE}" = "windows" ]; then
BINARY_PATH="packages/binpress/build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binpress.exe"
else
BINARY_PATH="packages/binpress/build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binpress"
fi
echo "BINARY_PATH=${BINARY_PATH}" >> $GITHUB_ENV
- name: Upload artifacts
if: steps.build-enabled.outputs.build == 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.BINARY_PATH }}
retention-days: 30
if-no-files-found: error
compression-level: 0 # Already-compressed binary artifact
# Job group 2: binflate
binflate:
permissions:
contents: read
id-token: write # OIDC authentication for Depot builds
name: binflate / ${{ matrix.platform == 'win32' && 'win' || 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 both darwin architectures: 8 vCPU M2, 24GB RAM
# Cross-compile x64 from ARM64 since Depot only offers ARM64 macOS
- runner: depot-macos-15
platform: darwin
arch: arm64
os: macos
- runner: depot-macos-15
platform: darwin
arch: x64
os: macos
cross_compile: true
# Linux glibc builds
- runner: ubuntu-24.04
platform: linux
arch: x64
os: linux
libc: glibc
- runner: ubuntu-24.04-arm
platform: linux
arch: arm64
os: linux
libc: glibc
# Linux musl builds (Alpine)
- runner: ubuntu-24.04
platform: linux
arch: x64
os: linux
libc: musl
- runner: ubuntu-24.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:
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@ea1986b8019fedee5fb38b485690b13ad8e0217f # main
- 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 "[OK] Building $TOOL (all tools selected)"
elif echo ",$TOOLS_INPUT," | grep -q ",$TOOL,"; then
echo "build=true" >> $GITHUB_OUTPUT
echo "[OK] 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 || '' }}
# Only create a signing keychain / install Depot CLI when a build is
# actually about to run. On cache-hit rounds the tarball already
# contains the signed binary, so spinning up an ephemeral keychain
# or fetching the Depot CLI just burns CI time.
- name: Setup macOS code signing
if: matrix.os == 'macos' && steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: ./.github/actions/setup-macos-keychain
- name: Setup Depot CLI
if: matrix.os == 'linux' && steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
with:
oidc: true
- name: Set platform-arch (binflate)
id: platform-arch
uses: ./.github/actions/set-platform-arch
with:
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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# All platforms (Linux Depot / macOS / Windows native) export to
# build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/. A prior version
# of this step had a separate Linux branch without PLATFORM_ARCH
# based on a misread of the Dockerfile — the binary would never
# be found on Linux cache-hit and force-rebuild kept firing,
# defeating the entire checkpoint cache for this matrix.
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/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 build environment
if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: ./.github/actions/setup-build-env
with:
os: ${{ matrix.os }}
arch: ${{ matrix.arch }}
libc: ${{ matrix.libc || '' }}
cross-compile: ${{ matrix.cross_compile || 'false' }}
- name: Debug build decision
if: steps.build-enabled.outputs.build == 'true'
shell: bash
env:
BUILD_REQUIRED: ${{ steps.setup-checkpoints.outputs.build-required }}
CACHE_HIT: ${{ steps.setup-checkpoints.outputs.cache-hit }}
run: |
if [ "${BUILD_REQUIRED}" = "true" ]; then
echo "✅ Build will run (cache-hit: ${CACHE_HIT})"
else
echo "⏭️ Skipping build (checkpoint restored)"
fi
- name: Load cache version (binflate)
id: cache-version
shell: bash
run: |
load_version() {
local key="$1"
local val
val=$(jq -r ".versions[\"$key\"]" .github/cache-versions.json)
if [ -z "$val" ] || [ "$val" = "null" ]; then
echo "error: cache-versions.json missing key: $key" >&2
exit 1
fi
echo "$val"
}
echo "version=$(load_version binflate)" >> $GITHUB_OUTPUT
echo "lief_version=$(load_version lief)" >> $GITHUB_OUTPUT
echo "stubs_version=$(load_version stubs)" >> $GITHUB_OUTPUT
- name: Initialize submodules before Docker build (Linux)
if: |
steps.build-enabled.outputs.build == 'true' &&
steps.setup-checkpoints.outputs.build-required == 'true' &&
matrix.os == 'linux'
uses: ./.github/actions/init-submodules
with:
# binflate links zstd only — libdeflate is binject-only.
# Previously inited both, wasting ~10-30s per Linux matrix
# entry on an unused clone.
submodules: packages/bin-infra/upstream/zstd
- name: Stage registry tool-checksums for Docker build context
if: |
steps.build-enabled.outputs.build == 'true' &&
steps.setup-checkpoints.outputs.build-required == 'true' &&
matrix.os == 'linux'
shell: bash
run: |
# Socket-registry's setup action exports SOCKET_TOOL_CHECKSUMS_FILE
# pointing at a stable on-runner copy of external-tools.json.
# Dockerfiles `COPY .build-context/registry-tools.json …` to
# resolve pnpm version + per-platform asset + sha256 at build
# time without duplicating per-Dockerfile constants.
mkdir -p .build-context
cp "$SOCKET_TOOL_CHECKSUMS_FILE" .build-context/registry-tools.json
- 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@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1.17.0
with:
project: 8fpj9495vw
platforms: linux/${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }}
build-args: |
BUILD_MODE=${{ steps.setup-checkpoints.outputs.build-mode }}
PLATFORM_ARCH=${{ steps.platform-arch.outputs.platform_arch }}
CACHE_VERSION=${{ steps.cache-version.outputs.version }}
LIEF_CACHE_VERSION=${{ steps.cache-version.outputs.lief_version }}
STUBS_CACHE_VERSION=${{ steps.cache-version.outputs.stubs_version }}
no-cache: ${{ inputs.force == true }}
file: packages/binflate/docker/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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
run: |
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binflate"
[ -f "$BINARY" ] || { echo "× Binary not found: $BINARY"; exit 1; }
echo "✅ $(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
platform-arch: ${{ steps.platform-arch.outputs.platform_arch }}
- 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: ${{ github.token }}
PLATFORM: ${{ matrix.platform }}
ARCH: ${{ matrix.arch }}
TARGET_ARCH: ${{ matrix.arch }}
DEBUG: ${{ inputs.debug != '0' && inputs.debug || '' }}
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"
# Construct PLATFORM_ARCH for build output path.
if [ "${PLATFORM}" = "win32" ]; then
RELEASE_PLATFORM="win"
else
RELEASE_PLATFORM="${PLATFORM}"
fi
PLATFORM_ARCH="${RELEASE_PLATFORM}-${ARCH}"
ls -lh build/${BUILD_MODE}/${PLATFORM_ARCH}/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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# All platforms (Linux Depot / macOS / Windows native) export to
# build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/. Matches the
# validate-checkpoint step above.
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binflate"
if [ "${OS_TYPE}" = "windows" ]; then
BINARY="${BINARY}.exe"
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' && matrix.os != 'linux'
shell: bash
working-directory: packages/binflate
env:
GH_TOKEN: ${{ github.token }}
run: |
# Run full functional tests on native x64 platforms (non-Linux).
# Linux glibc tests run inside Docker to avoid LTO bytecode incompatibility.
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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
ARCH: ${{ matrix.arch }}
LIBC: ${{ matrix.libc }}
run: |
# Deterministic smoke test: native execution, Docker, QEMU, or static verification
# All platforms now use PLATFORM_ARCH in build output path.
if [ "${OS_TYPE}" = "windows" ]; then
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binflate.exe"
else
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/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.mts $SMOKE_TEST_ARGS"
node ../../packages/build-infra/scripts/smoke-test-binary.mts $SMOKE_TEST_ARGS
- name: Set artifact name and path
shell: bash
env:
PLATFORM: ${{ matrix.platform }}
ARCH: ${{ matrix.arch }}
LIBC: ${{ matrix.libc }}
BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# Use platform-arch output (win32→win conversion already done).
ARTIFACT_NAME="binflate-${PLATFORM_ARCH}"
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
# Construct binary path.
# All platforms now use PLATFORM_ARCH in build output path.
if [ "${OS_TYPE}" = "windows" ]; then
BINARY_PATH="packages/binflate/build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binflate.exe"
else
BINARY_PATH="packages/binflate/build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binflate"
fi
echo "BINARY_PATH=${BINARY_PATH}" >> $GITHUB_ENV
- name: Upload artifacts
if: steps.build-enabled.outputs.build == 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.BINARY_PATH }}
retention-days: 30
if-no-files-found: error
compression-level: 0 # Already-compressed binary artifact
# Job group 3: binject
binject:
permissions:
contents: read
id-token: write # OIDC authentication for Depot builds
name: binject / ${{ matrix.platform == 'win32' && 'win' || 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 both darwin architectures: 8 vCPU M2, 24GB RAM
# Cross-compile x64 from ARM64 since Depot only offers ARM64 macOS
- runner: depot-macos-15
platform: darwin
arch: arm64
os: macos
- runner: depot-macos-15
platform: darwin
arch: x64
os: macos
cross_compile: true
# Linux glibc builds
- runner: ubuntu-24.04
platform: linux
arch: x64
os: linux
libc: glibc
- runner: ubuntu-24.04-arm
platform: linux
arch: arm64
os: linux
libc: glibc
# Linux musl builds (Alpine)
- runner: ubuntu-24.04
platform: linux
arch: x64
os: linux
libc: musl
- runner: ubuntu-24.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:
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@ea1986b8019fedee5fb38b485690b13ad8e0217f # main
- 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 "[OK] Building $TOOL (all tools selected)"
elif echo ",$TOOLS_INPUT," | grep -q ",$TOOL,"; then
echo "build=true" >> $GITHUB_OUTPUT
echo "[OK] 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 || '' }}
# Only create a signing keychain / install Depot CLI when a build is
# actually about to run. On cache-hit rounds the tarball already
# contains the signed binary, so spinning up an ephemeral keychain
# or fetching the Depot CLI just burns CI time.
- name: Setup macOS code signing
if: matrix.os == 'macos' && steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: ./.github/actions/setup-macos-keychain
- name: Setup Depot CLI
if: matrix.os == 'linux' && steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
with:
oidc: true
- name: Set platform-arch (binject)
id: platform-arch
uses: ./.github/actions/set-platform-arch
with:
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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# All platforms (Linux Depot / macOS / Windows native) export to
# build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/. A prior version
# of this step had a separate Linux branch without PLATFORM_ARCH
# based on a misread of the Dockerfile — the binary would never
# be found on Linux cache-hit and force-rebuild kept firing,
# defeating the entire checkpoint cache for this matrix.
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/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 build environment
if: steps.build-enabled.outputs.build == 'true' && steps.setup-checkpoints.outputs.build-required == 'true'
uses: ./.github/actions/setup-build-env
with:
os: ${{ matrix.os }}
arch: ${{ matrix.arch }}
libc: ${{ matrix.libc || '' }}
cross-compile: ${{ matrix.cross_compile || 'false' }}
- 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..."
# Retry up to 3 times to handle transient Chocolatey CDN failures
for i in 1 2 3; do
if choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y; then
break
fi
echo "Attempt $i failed, retrying in 10 seconds..."
sleep 10
done
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_REQUIRED: ${{ steps.setup-checkpoints.outputs.build-required }}
CACHE_HIT: ${{ steps.setup-checkpoints.outputs.cache-hit }}
run: |
if [ "${BUILD_REQUIRED}" = "true" ]; then
echo "✅ Build will run (cache-hit: ${CACHE_HIT})"
else
echo "⏭️ Skipping build (checkpoint restored)"
fi
- name: Load cache version (binject)
id: cache-version
shell: bash
run: |
load_version() {
local key="$1"
local val
val=$(jq -r ".versions[\"$key\"]" .github/cache-versions.json)
if [ -z "$val" ] || [ "$val" = "null" ]; then
echo "error: cache-versions.json missing key: $key" >&2
exit 1
fi
echo "$val"
}
echo "version=$(load_version binject)" >> $GITHUB_OUTPUT
echo "lief_version=$(load_version lief)" >> $GITHUB_OUTPUT
echo "stubs_version=$(load_version stubs)" >> $GITHUB_OUTPUT
- name: Initialize submodules before Docker build (Linux)
if: |
steps.build-enabled.outputs.build == 'true' &&
steps.setup-checkpoints.outputs.build-required == 'true' &&
matrix.os == 'linux'
uses: ./.github/actions/init-submodules
with:
submodules: packages/bin-infra/upstream/zstd packages/binject/upstream/libdeflate 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)
echo "libdeflate=${LIBDEFLATE_SHA}" >> $GITHUB_OUTPUT
echo "cjson=${CJSON_SHA}" >> $GITHUB_OUTPUT
echo "Submodule SHAs: libdeflate=${LIBDEFLATE_SHA}, cJSON=${CJSON_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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
run: rm -rf packages/lief-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/lief/
- name: Stage registry tool-checksums for Docker build context
if: |
steps.build-enabled.outputs.build == 'true' &&
steps.setup-checkpoints.outputs.build-required == 'true' &&
matrix.os == 'linux'
shell: bash
run: |
# Socket-registry's setup action exports SOCKET_TOOL_CHECKSUMS_FILE
# pointing at a stable on-runner copy of external-tools.json.
# Dockerfiles `COPY .build-context/registry-tools.json …` to
# resolve pnpm version + per-platform asset + sha256 at build
# time without duplicating per-Dockerfile constants.
mkdir -p .build-context
cp "$SOCKET_TOOL_CHECKSUMS_FILE" .build-context/registry-tools.json
- 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@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1.17.0
with:
project: 8fpj9495vw
platforms: linux/${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }}
build-args: |
BUILD_MODE=${{ steps.setup-checkpoints.outputs.build-mode }}
PLATFORM_ARCH=${{ steps.platform-arch.outputs.platform_arch }}
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 }}
STUBS_CACHE_VERSION=${{ steps.cache-version.outputs.stubs_version }}
# GH_TOKEN via BuildKit secret (see binpress build above).
secrets: |
gh_token=${{ github.token }}
no-cache: ${{ inputs.force == true }}
file: packages/binject/docker/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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
run: |
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binject"
[ -f "$BINARY" ] || { echo "× Binary not found: $BINARY"; exit 1; }
echo "✅ $(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
platform-arch: ${{ steps.platform-arch.outputs.platform_arch }}
- 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: ${{ github.token }}
PLATFORM: ${{ matrix.platform }}
ARCH: ${{ matrix.arch }}
TARGET_ARCH: ${{ matrix.arch }}
DEBUG: ${{ inputs.debug != '0' && inputs.debug || '' }}
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"
# Construct PLATFORM_ARCH for build output path.
if [ "${PLATFORM}" = "win32" ]; then
RELEASE_PLATFORM="win"
else
RELEASE_PLATFORM="${PLATFORM}"
fi
PLATFORM_ARCH="${RELEASE_PLATFORM}-${ARCH}"
ls -lh build/${BUILD_MODE}/${PLATFORM_ARCH}/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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# All platforms (Linux Depot / macOS / Windows native) export to
# build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/. Matches the
# validate-checkpoint step above.
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binject"
if [ "${OS_TYPE}" = "windows" ]; then
BINARY="${BINARY}.exe"
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' && matrix.os != 'linux'
shell: bash
working-directory: packages/binject
env:
GH_TOKEN: ${{ github.token }}
run: |
# Run full functional tests on native x64 platforms (non-Linux).
# Linux glibc tests run inside Docker to avoid LTO bytecode incompatibility.
echo "Running functional tests for binject..."
node scripts/test.mts
- 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 }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
ARCH: ${{ matrix.arch }}
LIBC: ${{ matrix.libc }}
run: |
# Deterministic smoke test: native execution, Docker, QEMU, or static verification
# All platforms now use PLATFORM_ARCH in build output path.
if [ "${OS_TYPE}" = "windows" ]; then
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binject.exe"
else
BINARY="build/${BUILD_MODE}/${PLATFORM_ARCH}/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.mts $SMOKE_TEST_ARGS"
node ../../packages/build-infra/scripts/smoke-test-binary.mts $SMOKE_TEST_ARGS
- name: Set artifact name and path
shell: bash
env:
PLATFORM: ${{ matrix.platform }}
ARCH: ${{ matrix.arch }}
LIBC: ${{ matrix.libc }}
BUILD_MODE: ${{ steps.setup-checkpoints.outputs.build-mode }}
PLATFORM_ARCH: ${{ steps.platform-arch.outputs.platform_arch }}
OS_TYPE: ${{ matrix.os }}
run: |
# Use platform-arch output (win32→win conversion already done).
ARTIFACT_NAME="binject-${PLATFORM_ARCH}"
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
# Construct binary path.
# All platforms now use PLATFORM_ARCH in build output path.
if [ "${OS_TYPE}" = "windows" ]; then
BINARY_PATH="packages/binject/build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binject.exe"
else
BINARY_PATH="packages/binject/build/${BUILD_MODE}/${PLATFORM_ARCH}/out/Final/binject"
fi
echo "BINARY_PATH=${BINARY_PATH}" >> $GITHUB_ENV
- name: Upload artifacts
if: steps.build-enabled.outputs.build == 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.BINARY_PATH }}
retention-days: 30
if-no-files-found: error
compression-level: 0 # Already-compressed binary artifact
release:
name: Release ${{ matrix.tool }}
needs: [binpress, binflate, binject]
# always() lets this job run when ANY of the three tool-build jobs
# completed — otherwise selecting `tools=binpress` would skip binflate
# and binject (their own job-level `if:` gates them off), and GitHub
# treats skipped dependencies as unsatisfied, silently canceling the
# entire release matrix. With always(), each release-matrix entry still
# evaluates its own `Check if tool should be released` step and skips
# the ones the user didn't ask for.
if: |
always()
&& (needs.binpress.result == 'success'
|| needs.binflate.result == 'success'
|| needs.binject.result == 'success')
&& github.event_name == 'workflow_dispatch'
&& !inputs.dry_run
environment: release
runs-on: ubuntu-24.04
timeout-minutes: 15
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 "[OK] Releasing $TOOL (all tools selected)"
elif echo "$TOOLS_INPUT" | grep -q "$TOOL"; then
echo "release=true" >> $GITHUB_OUTPUT
echo "[OK] 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}-win-x64" ]; then
mv artifacts/${TOOL}-win-x64/${TOOL}.exe \
dist/${TOOL}/${TOOL}-win-x64.exe 2>/dev/null || \
mv artifacts/${TOOL}-win-x64/${TOOL} \
dist/${TOOL}/${TOOL}-win-x64 2>/dev/null || true
fi
# Windows ARM64
if [ -d "artifacts/${TOOL}-win-arm64" ]; then
mv artifacts/${TOOL}-win-arm64/${TOOL}.exe \
dist/${TOOL}/${TOOL}-win-arm64.exe 2>/dev/null || \
mv artifacts/${TOOL}-win-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
env:
GH_TOKEN: ${{ github.token }}
TOOL: ${{ matrix.tool }}
run: |
source .github/scripts/generate-version.sh
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
TAG="${TOOL}-${VERSION}"
if gh release view "$TAG" &>/dev/null; then
echo "Release $TAG already exists, skipping."
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Generate checksums
if: steps.should-release.outputs.release == 'true' && steps.version.outputs.skip != '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' && steps.version.outputs.skip != '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
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
update-checksums:
needs: release
# always() + explicit success check: without always(), a skipped release
# matrix entry (e.g. when the user selected tools=binpress and binflate/
# binject were filtered out) makes GitHub treat `needs.release` as
# unsatisfied and silently cancels this job — so the checksum bump
# never lands even though the published tool needs one. Same pattern
# as the release job's R8 fix.
if: |
always()
&& needs.release.result == 'success'
&& github.event_name == 'workflow_dispatch'
&& !inputs.dry_run
permissions:
contents: write # release-assets.json commit after publishing GitHub release
uses: ./.github/workflows/update-checksums.yml
secrets:
BOT_GPG_PRIVATE_KEY: ${{ secrets.BOT_GPG_PRIVATE_KEY }}