working on arm64 native
Some checks failed
CI / Rustfmt (push) Successful in 24s
CI / Cargo Audit & Deny (push) Successful in 36s
CI / Security Blocking Checks (push) Successful in 9s
CI / Web Blocking Checks (push) Successful in 48s
CI / Web Advisory Checks (push) Successful in 37s
Publish Images / Resolve Publish Metadata (push) Successful in 2s
CI / Clippy (push) Failing after 1m53s
Publish Images / Publish Docker Dist Bundle (push) Failing after 8s
Publish Images / Publish web (amd64) (push) Successful in 56s
CI / Security Advisory Checks (push) Successful in 38s
Publish Images / Publish web (arm64) (push) Successful in 3m29s
CI / Tests (push) Successful in 9m21s
Publish Images / Build Rust Bundles (amd64) (push) Failing after 12m28s
Publish Images / Build Rust Bundles (arm64) (push) Successful in 12m20s
Publish Images / Publish agent (amd64) (push) Has been skipped
Publish Images / Publish api (amd64) (push) Has been skipped
Publish Images / Publish agent (arm64) (push) Has been skipped
Publish Images / Publish api (arm64) (push) Has been skipped
Publish Images / Publish executor (amd64) (push) Has been skipped
Publish Images / Publish notifier (amd64) (push) Has been skipped
Publish Images / Publish executor (arm64) (push) Has been skipped
Publish Images / Publish notifier (arm64) (push) Has been skipped
Publish Images / Publish manifest attune/agent (push) Has been skipped
Publish Images / Publish manifest attune/api (push) Has been skipped
Publish Images / Publish manifest attune/notifier (push) Has been skipped
Publish Images / Publish manifest attune/executor (push) Has been skipped
Publish Images / Publish manifest attune/web (push) Has been skipped

This commit is contained in:
David Culbreth
2026-03-27 16:37:46 -05:00
parent 3a13bf754a
commit 7ef2b59b23
16 changed files with 553 additions and 159 deletions

View File

@@ -0,0 +1,101 @@
# Pack Binaries: Cross-Architecture Static Build
**Date**: 2026-03-27
## Problem
The `attune-core-timer-sensor` native sensor binary failed to execute in Docker containers on Apple Silicon (arm64) Macs with the error:
```
rosetta error: failed to open elf at /lib64/ld-linux-x86-64.so.2
```
**Two root causes**:
1. **Wrong build toolchain**: `docker/Dockerfile.pack-binaries` used a plain `cargo build` which produced a **dynamically-linked, host-architecture** binary. On arm64 Docker hosts, this created an aarch64 binary linked against glibc. When the sensor container tried to execute it, the required dynamic linker (`ld-linux-x86-64.so.2`) was absent. This contrasted with `docker/Dockerfile.agent`, which already used `cargo-zigbuild` + musl for fully static binaries.
2. **init-packs overwrote the static binary**: Even after fixing the Dockerfile, the `init-packs.sh` script did `cp -rf` from `./packs/` (host bind mount) into the packs volume, **overwriting** the freshly-placed static binary from `init-pack-binaries` with the old dynamically-linked binary from the host's `packs/core/sensors/` directory.
## Changes
### `docker/Dockerfile.pack-binaries` — Rewritten for static cross-compilation
- Added `RUST_TARGET` build arg (default: `x86_64-unknown-linux-musl`)
- Installed `musl-tools`, `ziglang`, and `cargo-zigbuild` (matching agent Dockerfile pattern)
- Replaced `cargo build --release` with `cargo zigbuild --release --target ${RUST_TARGET}`
- Added `cargo fetch` dependency caching layer with proper workspace stubs (including sensor `agent_main.rs`)
- Added `SQLX_OFFLINE=true` for compile-time query checking without a live database
- Added strip-with-fallback for cross-arch scenarios
- Added **Stage 3: `pack-binaries-init`** — busybox-based image for Docker Compose volume population (analogous to `agent-init`)
- Updated cache ID to `target-pack-binaries-static` with `sharing=locked` for zigbuild exclusivity
### `docker/init-packs.sh` — Preserve static binaries during pack copy
- Before copying host pack files, detects ELF binaries already present in the target `sensors/` directory using the 4-byte ELF magic number (`\x7fELF` = `7f454c46`) via `od` (available in python:3.11-slim, unlike `file`)
- Backs up detected ELF binaries to a temp directory before the `cp -rf` overwrites them
- Restores the backed-up static binaries after the copy completes
- Logs each preserved binary for visibility
### `docker-compose.yaml` — Added `init-pack-binaries` service
- New `init-pack-binaries` service builds from `Dockerfile.pack-binaries` (target: `pack-binaries-init`) and copies the static binary into the `packs_data` volume
- Accepts `PACK_BINARIES_RUST_TARGET` env var (default: `x86_64-unknown-linux-musl`)
- `init-packs` now depends on `init-pack-binaries` to ensure binaries are in the volume before pack files are copied
- `docker compose up` now automatically builds and deploys pack binaries — no manual script run required
### `docker/distributable/docker-compose.yaml` — Same pattern for distributable
- Added `init-pack-binaries` service using pre-built registry image
- Updated `init-packs` dependencies
### `scripts/build-pack-binaries.sh` — Updated for static builds
- Passes `RUST_TARGET` build arg to Docker build
- Accepts `RUST_TARGET` env var (default: `x86_64-unknown-linux-musl`)
- Updated verification output to expect statically-linked binary
### `Makefile` — New targets
- `PACK_BINARIES_RUST_TARGET` variable (default: `x86_64-unknown-linux-musl`)
- `docker-build-pack-binaries` — build for default architecture
- `docker-build-pack-binaries-arm64` — build for aarch64
- `docker-build-pack-binaries-all` — build both architectures
### `.gitignore` — Exclude compiled pack binary
- Added `packs/core/sensors/attune-core-timer-sensor` to `.gitignore`
- Removed the stale dynamically-linked binary from git tracking
## Architecture
The fix follows the same proven pattern as `Dockerfile.agent`:
```
cargo-zigbuild + musl → statically-linked binary → zero runtime dependencies
```
Since the binary has no dynamic library dependencies (no glibc, no libssl, no dynamic linker), it runs on **any** Linux container of the matching CPU architecture, regardless of the base image (Debian, Alpine, scratch, etc.).
### Init sequence
1. **`init-pack-binaries`**: Builds static musl binary → copies to `packs_data` volume at `core/sensors/`
2. **`init-packs`** (depends on `init-pack-binaries`): Copies host `./packs/core/` to volume → detects existing ELF binary → backs it up → copies host files → restores static binary
3. **`sensor`**: Spawns the static `attune-core-timer-sensor` → works on any architecture
## Usage
```bash
# Default (x86_64) — works on amd64 containers and arm64 via Rosetta
docker compose up -d
# For native arm64 containers
PACK_BINARIES_RUST_TARGET=aarch64-unknown-linux-musl docker compose up -d
# Standalone build
make docker-build-pack-binaries # amd64
make docker-build-pack-binaries-arm64 # arm64
make docker-build-pack-binaries-all # both
# Manual script
RUST_TARGET=aarch64-unknown-linux-musl ./scripts/build-pack-binaries.sh
```