diff --git a/container/Containerfile b/container/Containerfile index 4634b52f..740b0417 100644 --- a/container/Containerfile +++ b/container/Containerfile @@ -46,10 +46,23 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ # sccache — shared compilation cache. Enabled at build time via # RUSTC_WRAPPER (see the CI workflow and .devcontainer), not repo-wide, so # non-SDK builds are unaffected. musl build is static and runs anywhere. +# +# The release asset is per-architecture, so resolve it from `uname -m` rather +# than hardcoding one triple: everything else in this image is arch-agnostic, +# and a pinned x86_64 URL is what forces an amd64 build (and Rosetta or qemu) +# on an arm64 host. `uname -m` reflects the build platform under plain +# docker/podman build as well as buildx, unlike the BuildKit-only TARGETARCH. ARG SCCACHE_VERSION=0.8.2 -RUN curl -fsSL "https://github.com/mozilla/sccache/releases/download/v${SCCACHE_VERSION}/sccache-v${SCCACHE_VERSION}-x86_64-unknown-linux-musl.tar.gz" \ - | tar -xz -C /tmp \ - && install -m755 "/tmp/sccache-v${SCCACHE_VERSION}-x86_64-unknown-linux-musl/sccache" /usr/local/bin/sccache \ - && rm -rf /tmp/sccache-* +RUN set -eux; \ + case "$(uname -m)" in \ + x86_64) sccache_arch=x86_64 ;; \ + aarch64|arm64) sccache_arch=aarch64 ;; \ + *) echo "unsupported architecture for sccache: $(uname -m)" >&2; exit 1 ;; \ + esac; \ + sccache_dist="sccache-v${SCCACHE_VERSION}-${sccache_arch}-unknown-linux-musl"; \ + curl -fsSL "https://github.com/mozilla/sccache/releases/download/v${SCCACHE_VERSION}/${sccache_dist}.tar.gz" \ + | tar -xz -C /tmp; \ + install -m755 "/tmp/${sccache_dist}/sccache" /usr/local/bin/sccache; \ + rm -rf /tmp/sccache-* WORKDIR /work diff --git a/container/README.md b/container/README.md index 76c8cf91..fce6d67f 100644 --- a/container/README.md +++ b/container/README.md @@ -17,6 +17,13 @@ and `clippy` are identical everywhere — no "works on my machine". ## Build and publish +Nothing in the image is architecture-specific: the base image, the Debian build +dependencies, Node.js, `rustup` and the `sccache` release all resolve per +architecture, so the same `Containerfile` builds natively on x86_64 and arm64. + +Single architecture — the tag then only works on the architecture you built it +on: + ```bash # from the repo root podman build -t git.haxx.space/sjg/trx-rs/sdk:latest container @@ -24,12 +31,48 @@ podman login git.haxx.space podman push git.haxx.space/sjg/trx-rs/sdk:latest ``` +**Both architectures without emulation.** The CI runner is x86_64 and Apple +Silicon developer machines are arm64, so `:latest` has to be a manifest list — +a single-architecture tag makes the other side fall back to Rosetta or qemu. +Build each half natively on a host of that architecture, then join them: + +```bash +# on an x86_64 host +podman build --platform linux/amd64 -t git.haxx.space/sjg/trx-rs/sdk:latest-amd64 container +podman push git.haxx.space/sjg/trx-rs/sdk:latest-amd64 + +# on an arm64 host +podman build --platform linux/arm64 -t git.haxx.space/sjg/trx-rs/sdk:latest-arm64 container +podman push git.haxx.space/sjg/trx-rs/sdk:latest-arm64 + +# from either, once both are pushed +podman manifest create git.haxx.space/sjg/trx-rs/sdk:latest \ + git.haxx.space/sjg/trx-rs/sdk:latest-amd64 \ + git.haxx.space/sjg/trx-rs/sdk:latest-arm64 +podman manifest push --all git.haxx.space/sjg/trx-rs/sdk:latest +``` + +Building both from one machine is a single command +(`podman build --platform linux/amd64,linux/arm64 --manifest ...`), but the +foreign half runs under emulation and is slow — the two-host flow above is +what keeps every build native. + Tag with the Rust version too (e.g. `:1.97.1`) if you want reproducible pins. Make the package **public** (Gitea → Packages → the image → Settings) so the CI runner and developers can pull it without credentials. If you keep it private, add `credentials:` under the workflow's `container:` and log the runner into the registry. +### macOS note + +Apple's `container` CLI builds through a BuildKit helper VM that is configured +with Rosetta whether or not the target is x86_64, so `container build` fails +with *"Rosetta is not installed"* on a clean machine. That is a property of the +builder, not of this image — `container run` works natively without it. Either +install Rosetta once (`softwareupdate --install-rosetta`, after which an arm64 +build still produces a native arm64 image), or build with Podman, whose arm64 +BuildKit needs no emulation. + ## Developer use Reproducible one-off build, no local toolchain needed: @@ -111,8 +154,10 @@ adduser -S -D -H -h /var/lib/act_runner act addgroup act docker # 2. act_runner binary (static Go build, works on musl) +# Upstream publishes per-architecture builds; pick the host's. +case "$(uname -m)" in x86_64) arch=amd64 ;; aarch64) arch=arm64 ;; esac curl -fsSL -o /usr/local/bin/act_runner \ - https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64 + "https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-${arch}" chmod +x /usr/local/bin/act_runner # 3. Config + register one runner per project (scope keeps their jobs apart)