fabro/bin/dev/docker-build.sh
Bryan Helmkamp 8da81fdf9e
fix(release): build musl targets via cargo-zigbuild to fix arm64 SIGSEGV
The shipped aarch64-unknown-linux-musl binary segfaulted at startup on
every arm64 runtime (Apple Silicon, Graviton, Ampere, Docker arm64).
Root cause: a glibc-vs-musl .init_array calling-convention mismatch --
a C static library in the dep graph has an __attribute__((constructor))
that expects (argc, argv, envp) per glibc, but musl on aarch64 calls
it with no args, so register garbage propagates into pointer arithmetic
and faults before main runs.

Switch the musl compile steps to cargo-zigbuild (zig 0.13.0). Zig's
bundled cc + lld produce working static-PIE binaries for both musl
targets, sidestepping Ubuntu musl-tools' -no-pie quirk and the
init_array ordering that triggered the crash. Drop the CARGO_TARGET_*
linker overrides and the musl-tools apt install -- zig handles both.

bin/dev/docker-build.sh mirrors the same toolchain so the local Docker
image build matches CI.

Verified by running fabro version from the resulting arm64 image on
ghcr.io/fabro-sh/dhi-alpine-base:3.23-dev, alpine:3.22, and
debian:stable-slim -- all print the version banner with exit 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:01:45 -04:00

94 lines
3 KiB
Bash
Executable file

#!/usr/bin/env bash
# Build a local fabro Docker image from the current working tree.
#
# The Docker image uses musl binaries for a small Alpine-based runtime. We
# compile fabro-cli for the target musl triple inside rust:1-bookworm using
# cargo-zigbuild (zig as the C compiler + linker). Cargo registry and target
# dir are cached in named volumes so subsequent runs are incremental.
#
# Usage:
# bin/dev/docker-build.sh # host arch, build image at end
# bin/dev/docker-build.sh --arch amd64 # force amd64 (uses QEMU on non-amd64 hosts — slow)
# bin/dev/docker-build.sh --arch arm64 --compile-only
# # stage binary only; skip `docker build`
# # (useful for multi-arch buildx verification)
set -euo pipefail
cd "$(dirname "$0")/../.."
arch=""
compile_only=0
while [ $# -gt 0 ]; do
case "$1" in
--arch)
arch="$2"
shift 2
;;
--compile-only)
compile_only=1
shift
;;
*)
echo "unknown argument: $1" >&2
exit 1
;;
esac
done
if [ -z "$arch" ]; then
case "$(uname -m)" in
x86_64|amd64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "unsupported host arch: $(uname -m)" >&2; exit 1 ;;
esac
fi
case "$arch" in
amd64) target=x86_64-unknown-linux-musl zig_arch=x86_64 ;;
arm64) target=aarch64-unknown-linux-musl zig_arch=aarch64 ;;
*) echo "unsupported arch: $arch (expected amd64 or arm64)" >&2; exit 1 ;;
esac
ZIG_VERSION=0.13.0
echo "Building fabro-cli for $target inside rust:1-bookworm via cargo-zigbuild..."
docker run --rm --platform "linux/$arch" \
-v "$PWD:/src" \
-v fabro-docker-cargo-registry:/usr/local/cargo/registry \
-v "fabro-docker-cargo-target-$arch:/target" \
-v "fabro-docker-zig-$arch:/opt/zig" \
-v "fabro-docker-cargo-tools-$arch:/opt/cargo-tools" \
-w /src \
-e CARGO_TARGET_DIR=/target \
-e LIBZ_SYS_STATIC=1 \
rust:1-bookworm \
bash -c "
set -e
apt-get update -qq && apt-get install -y -qq pkg-config perl make cmake xz-utils curl >/dev/null
if [ ! -x /opt/zig/zig-linux-$zig_arch-$ZIG_VERSION/zig ]; then
curl -fsSL https://ziglang.org/download/$ZIG_VERSION/zig-linux-$zig_arch-$ZIG_VERSION.tar.xz | tar -xJ -C /opt/zig
fi
export PATH=/opt/cargo-tools/bin:/opt/zig/zig-linux-$zig_arch-$ZIG_VERSION:\$PATH
if ! command -v cargo-zigbuild >/dev/null; then
cargo install --locked --root /opt/cargo-tools cargo-zigbuild
fi
rustup target add $target
cargo zigbuild --release -p fabro-cli --target $target
"
echo "Extracting binary from builder cache..."
mkdir -p "docker-context/$arch"
docker run --rm --platform "linux/$arch" \
-v "fabro-docker-cargo-target-$arch:/target" \
-v "$PWD/docker-context/$arch:/out" \
rust:1-bookworm \
cp "/target/$target/release/fabro" /out/fabro
if [ "$compile_only" -eq 1 ]; then
echo "Staged docker-context/$arch/fabro (skipping docker build per --compile-only)."
exit 0
fi
echo "Building Docker image..."
docker build --platform "linux/$arch" -t fabro .