Skip to content

feat: arm64 xgoreleaser docker image - #24

Merged
aeneasr merged 7 commits into
masterfrom
arm-support
Aug 13, 2026
Merged

feat: arm64 xgoreleaser docker image#24
aeneasr merged 7 commits into
masterfrom
arm-support

Conversation

@aeneasr

@aeneasr aeneasr commented Mar 3, 2026

Copy link
Copy Markdown
Member

No description provided.

aeneasr and others added 7 commits March 3, 2026 15:35
On arm64 build hosts, linux-cgo-amd64 and alpine-cgo-amd64 goreleaser
builds failed because CC resolved to the native aarch64-gcc, which does
not support the -m64 flag Go passes when targeting linux/amd64.

- Add CC=x86_64-linux-gnu-gcc to linux-cgo-amd64 (native on amd64,
  cross-compiler on arm64 via gcc-x86-64-linux-gnu package)
- Change CC=musl-gcc to CC=x86_64-linux-musl-gcc for alpine-cgo-amd64
  to use a consistent, architecture-aware name
- Add a --platform=linux/amd64 Docker stage to capture the x86_64 musl
  sysroot (startup files + libc) and copy it into the final image
- On amd64: symlink x86_64-linux-musl-gcc -> musl-gcc (no change in
  behaviour)
- On arm64: install gcc-x86-64-linux-gnu from apt and create a wrapper
  script x86_64-linux-musl-gcc that invokes it with -B/-L pointing at
  the musl sysroot

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous approach used FROM --platform=linux/amd64 to extract the
x86_64 musl sysroot, but that fails on arm64 CI runners without QEMU
(exec format error).

Instead, download x86_64-linux-musl-cross.tgz from musl.cc and extract
only the x86_64-linux-musl/ subdirectory (crt files, libc, headers).
These are x86_64 target files — not host executables — so no QEMU is
needed to extract or use them. The x86_64-linux-gnu-gcc cross-compiler
from apt provides the actual compiler binary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The tarball contains hard links in x86_64-linux-musl/bin/ that point to
files in bin/ (the cross-compiler executables). Extracting only the
x86_64-linux-musl/ subtree fails because the hard-link targets are not
present.

Extract the whole tarball, move only the sysroot part, then delete the
rest. All within a single RUN layer so the final image is not bloated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ive CI runners

Install libc6-dev-amd64-cross on arm64 to provide x86_64 glibc headers
(bits/libc-header-start.h) needed by x86_64-linux-gnu-gcc cross-compiler.

Split docker-publish workflow into per-arch native builds to avoid QEMU
emulation, then combine with a manifest job.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adding /aarch64-linux-musl-cross/bin to PATH exposes unprefixed `ld`
which shadows the system linker. When aarch64-linux-gnu-gcc invokes
the linker for glibc builds, it finds the musl ld which doesn't
support dynamic loading (needed for LTO plugins).

Symlink only the prefixed tools (aarch64-linux-musl-*, arm-linux-musleabihf-*)
into /usr/local/bin so goreleaser finds them without PATH conflicts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… arm64 hosts

On arm64 build hosts, the arm-linux-musleabihf-cross tarball from musl.cc
contains x86_64-hosted compiler binaries that cannot run natively. Extract
just the musl sysroot and wrap the already-installed gcc-arm-linux-gnueabihf
(Debian package) using the same -B/-L/-isystem pattern as the x86_64 musl
wrapper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds arm64 support for the oryd/xgoreleaser Docker image by making the image build architecture-aware, updating the embedded toolchains, and extending CI to build/test/publish for both amd64 and arm64.

Changes:

  • Update the Dockerfile to download the correct GoReleaser binaries per target architecture and install arch-specific musl toolchains.
  • Adjust build.tmpl.yml to use explicitly prefixed compilers for amd64 (glibc + musl) builds.
  • Update GitHub Actions workflows to build/test on amd64 + arm64 and publish multi-arch images via a manifest job.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
build.tmpl.yml Switches amd64 CC values to explicit, prefixed toolchain binaries.
Dockerfile Makes GoReleaser/toolchain setup depend on TARGETARCH and adds arm64-specific musl sysroot/compiler setup.
.github/workflows/docker-test.yml Runs PR build/test on both amd64 and arm64 runners.
.github/workflows/docker-publish.yml Builds/pushes per-arch images and creates/pushes multi-arch manifests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

docker buildx create --use
go_version="${{ github.event.inputs.golang }}"
goreleaser_version="${{ github.event.inputs.goreleaser }}"
arch="${{ matrix.platform##*/}}"

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch="${{ matrix.platform##*/}}" is not valid GitHub Actions expression syntax (the ##*/ parameter expansion is Bash-only). This will fail workflow parsing or produce an empty value, resulting in incorrect tags. Compute arch in Bash from ${{ matrix.platform }} (e.g., assign the platform string first, then use shell parameter expansion), or define arch as a separate matrix field.

Suggested change
arch="${{ matrix.platform##*/}}"
platform="${{ matrix.platform }}"
arch="${platform##*/}"

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile
Comment on lines +109 to +124
RUN case "${TARGETARCH}" in \
amd64) GR_ARCH=x86_64 ;; \
arm64) GR_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}"; exit 1 ;; \
esac \
&& curl -LO https://github.com/goreleaser/goreleaser/releases/download/v${GORELEASER_VERSION}/goreleaser_Linux_${GR_ARCH}.tar.gz \
&& mkdir -p goreleaser_Linux_${GR_ARCH} \
&& tar -xvf goreleaser_Linux_${GR_ARCH}.tar.gz -C goreleaser_Linux_${GR_ARCH} \
&& mv goreleaser_Linux_${GR_ARCH}/goreleaser /usr/local/bin/goreleaser-oss \
&& rm -rf goreleaser_Linux_${GR_ARCH}.* goreleaser_Linux_${GR_ARCH}/

RUN case "${TARGETARCH}" in \
amd64) GR_ARCH=x86_64 ;; \
arm64) GR_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}"; exit 1 ;; \
esac \

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Dockerfile now hard-fails if TARGETARCH is unset. TARGETARCH is only guaranteed when building with BuildKit/buildx; if someone builds with the legacy builder (or with BuildKit disabled), ${TARGETARCH} will be empty and the case will exit 1. Consider adding a robust fallback (e.g., derive the arch from dpkg --print-architecture/uname -m) or defaulting safely so local docker build keeps working.

Suggested change
RUN case "${TARGETARCH}" in \
amd64) GR_ARCH=x86_64 ;; \
arm64) GR_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}"; exit 1 ;; \
esac \
&& curl -LO https://github.com/goreleaser/goreleaser/releases/download/v${GORELEASER_VERSION}/goreleaser_Linux_${GR_ARCH}.tar.gz \
&& mkdir -p goreleaser_Linux_${GR_ARCH} \
&& tar -xvf goreleaser_Linux_${GR_ARCH}.tar.gz -C goreleaser_Linux_${GR_ARCH} \
&& mv goreleaser_Linux_${GR_ARCH}/goreleaser /usr/local/bin/goreleaser-oss \
&& rm -rf goreleaser_Linux_${GR_ARCH}.* goreleaser_Linux_${GR_ARCH}/
RUN case "${TARGETARCH}" in \
amd64) GR_ARCH=x86_64 ;; \
arm64) GR_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}"; exit 1 ;; \
esac \
RUN ARCH="${TARGETARCH}" \
&& if [ -z "${ARCH}" ]; then \
if command -v dpkg >/dev/null 2>&1; then \
ARCH="$(dpkg --print-architecture)"; \
else \
ARCH="$(uname -m)"; \
fi; \
fi \
&& case "${ARCH}" in \
amd64|x86_64) GR_ARCH=x86_64 ;; \
arm64|aarch64) GR_ARCH=arm64 ;; \
*) echo "Unsupported architecture: ${ARCH}"; exit 1 ;; \
esac \
&& curl -LO https://github.com/goreleaser/goreleaser/releases/download/v${GORELEASER_VERSION}/goreleaser_Linux_${GR_ARCH}.tar.gz \
&& mkdir -p goreleaser_Linux_${GR_ARCH} \
&& tar -xvf goreleaser_Linux_${GR_ARCH}.tar.gz -C goreleaser_Linux_${GR_ARCH} \
&& mv goreleaser_Linux_${GR_ARCH}/goreleaser /usr/local/bin/goreleaser-oss \
&& rm -rf goreleaser_Linux_${GR_ARCH}.* goreleaser_Linux_${GR_ARCH}/
RUN ARCH="${TARGETARCH}" \
&& if [ -z "${ARCH}" ]; then \
if command -v dpkg >/dev/null 2>&1; then \
ARCH="$(dpkg --print-architecture)"; \
else \
ARCH="$(uname -m)"; \
fi; \
fi \
&& case "${ARCH}" in \
amd64|x86_64) GR_ARCH=x86_64 ;; \
arm64|aarch64) GR_ARCH=arm64 ;; \
*) echo "Unsupported architecture: ${ARCH}"; exit 1 ;; \
esac \

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile
Comment on lines +109 to +129
RUN case "${TARGETARCH}" in \
amd64) GR_ARCH=x86_64 ;; \
arm64) GR_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}"; exit 1 ;; \
esac \
&& curl -LO https://github.com/goreleaser/goreleaser/releases/download/v${GORELEASER_VERSION}/goreleaser_Linux_${GR_ARCH}.tar.gz \
&& mkdir -p goreleaser_Linux_${GR_ARCH} \
&& tar -xvf goreleaser_Linux_${GR_ARCH}.tar.gz -C goreleaser_Linux_${GR_ARCH} \
&& mv goreleaser_Linux_${GR_ARCH}/goreleaser /usr/local/bin/goreleaser-oss \
&& rm -rf goreleaser_Linux_${GR_ARCH}.* goreleaser_Linux_${GR_ARCH}/

RUN case "${TARGETARCH}" in \
amd64) GR_ARCH=x86_64 ;; \
arm64) GR_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}"; exit 1 ;; \
esac \
&& curl -Lo "goreleaser-pro_Linux_${GR_ARCH}.tar.gz" "https://github.com/goreleaser/goreleaser-pro/releases/download/v${GORELEASER_VERSION}/goreleaser-pro_Linux_${GR_ARCH}.tar.gz" \
&& mkdir -p goreleaser-pro_Linux_${GR_ARCH} \
&& tar -xvf goreleaser-pro_Linux_${GR_ARCH}.tar.gz -C goreleaser-pro_Linux_${GR_ARCH} \
&& mv goreleaser-pro_Linux_${GR_ARCH}/goreleaser /usr/local/bin/goreleaser \
&& rm -rf goreleaser-pro_Linux_${GR_ARCH}.* goreleaser-pro_Linux_${GR_ARCH}/

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The curl commands here download goreleaser binaries from GitHub and immediately extract and install them without any checksum or signature verification. If an attacker compromises DNS/TLS, the release assets, or the GitHub repository, they could serve a malicious binary that runs in the build environment and compromises all produced artifacts. Add integrity verification (e.g., pinned checksums or GPG signature verification) for the goreleaser archives before extraction, or vendor the binaries, to ensure only trusted code is executed.

Copilot uses AI. Check for mistakes.
@aeneasr
aeneasr merged commit fc3de6b into master Aug 13, 2026
8 checks passed
@aeneasr
aeneasr deleted the arm-support branch August 13, 2026 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants