[chore](trx-rs): build the SDK image natively on x86_64 and arm64
The sccache release asset is per-architecture and the Containerfile hardcoded the x86_64 triple, so an arm64 build produced an image whose sccache binary could not execute. Everything else in the image — the Debian base, the build dependencies, Node.js and rustup — already resolves per architecture, so that one URL was what pinned the image to amd64 and forced Rosetta or qemu on Apple Silicon. Resolve the triple from `uname -m`, which reflects the build platform under plain docker/podman build as well as buildx, unlike the BuildKit-only TARGETARCH. Document publishing `:latest` as a manifest list built natively on a host of each architecture, since a single-architecture tag sends the other side back to emulation, and note that Apple's `container` CLI needs Rosetta for its BuildKit helper VM regardless of the target. Pick the act_runner download by architecture for the same reason. Verified on arm64: the case arm selects sccache-v0.8.2-aarch64-unknown-linux-musl, and the installed binary reports `sccache 0.8.2` running natively. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
+17
-4
@@ -46,10 +46,23 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
|||||||
# sccache — shared compilation cache. Enabled at build time via
|
# sccache — shared compilation cache. Enabled at build time via
|
||||||
# RUSTC_WRAPPER (see the CI workflow and .devcontainer), not repo-wide, so
|
# RUSTC_WRAPPER (see the CI workflow and .devcontainer), not repo-wide, so
|
||||||
# non-SDK builds are unaffected. musl build is static and runs anywhere.
|
# 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
|
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" \
|
RUN set -eux; \
|
||||||
| tar -xz -C /tmp \
|
case "$(uname -m)" in \
|
||||||
&& install -m755 "/tmp/sccache-v${SCCACHE_VERSION}-x86_64-unknown-linux-musl/sccache" /usr/local/bin/sccache \
|
x86_64) sccache_arch=x86_64 ;; \
|
||||||
&& rm -rf /tmp/sccache-*
|
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
|
WORKDIR /work
|
||||||
|
|||||||
+46
-1
@@ -17,6 +17,13 @@ and `clippy` are identical everywhere — no "works on my machine".
|
|||||||
|
|
||||||
## Build and publish
|
## 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
|
```bash
|
||||||
# from the repo root
|
# from the repo root
|
||||||
podman build -t git.haxx.space/sjg/trx-rs/sdk:latest container
|
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
|
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.
|
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
|
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,
|
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
|
add `credentials:` under the workflow's `container:` and log the runner into the
|
||||||
registry.
|
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
|
## Developer use
|
||||||
|
|
||||||
Reproducible one-off build, no local toolchain needed:
|
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
|
addgroup act docker
|
||||||
|
|
||||||
# 2. act_runner binary (static Go build, works on musl)
|
# 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 \
|
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
|
chmod +x /usr/local/bin/act_runner
|
||||||
|
|
||||||
# 3. Config + register one runner per project (scope keeps their jobs apart)
|
# 3. Config + register one runner per project (scope keeps their jobs apart)
|
||||||
|
|||||||
Reference in New Issue
Block a user