Cross compilation helper for GoReleaser
This project is not maintained anymore and is abandoned. Feel free to fork and
make your own changes if needed.
goreleaser-xx
is a small CLI wrapper for GoReleaser
and available as a lightweight and multi-platform scratch Docker image
to ease the integration and cross compilation in a Dockerfile for your Go
projects.
Building yasu with
goreleaser-xx
--platform
in your Dockerfile for multi-platform support.goreleaser.yml
config based on target architectureRegistry | Image |
---|---|
Docker Hub | crazymax/goreleaser-xx |
GitHub Container Registry | ghcr.io/crazy-max/goreleaser-xx |
Following platforms for this image are available:
$ docker run --rm mplatform/mquery crazymax/goreleaser-xx:latest
Image: crazymax/goreleaser-xx:latest (digest: sha256:c65c481c014abab6d307b190ddf1fcb229a44b6c1845d2f2a53bd06dc0437cd7)
* Manifest List: Yes (Image type: application/vnd.docker.distribution.manifest.list.v2+json)
* Supported platforms:
- linux/386
- linux/amd64
- linux/arm/v5
- linux/arm/v6
- linux/arm/v7
- linux/arm64
- linux/ppc64le
- linux/riscv64
- linux/s390x
docker run --rm -t crazymax/goreleaser-xx:latest goreleaser-xx --help
Flag | Env var | Description |
---|---|---|
--debug |
DEBUG |
Enable debug (default false ) |
--git-ref |
GIT_REF |
The branch or tag like refs/tags/v1.0.0 (default to your working tree info) |
--goreleaser |
GORELEASER_PATH |
Set a specific GoReleaser binary to use (default goreleaser ) |
--config |
GORELEASER_CONFIG |
Load GoReleaser configuration from file |
--go-binary |
GORELEASER_GOBINARY |
Set a specific go binary to use when building (default go ) |
--name |
GORELEASER_NAME |
Project name |
--dist |
GORELEASER_DIST |
Dist folder where artifact will be stored |
--artifacts |
GORELEASER_ARTIFACTS |
Types of artifact to create (archive , bin ) (default archive ) |
--main |
GORELEASER_MAIN |
Path to main.go file or main package (default . ) |
--flags |
GORELEASER_FLAGS |
Custom flags templates |
--asmflags |
GORELEASER_ASMFLAGS |
Custom asmflags templates |
--gcflags |
GORELEASER_GCFLAGS |
Custom gcflags templates |
--ldflags |
GORELEASER_LDFLAGS |
Custom ldflags templates |
--tags |
GORELEASER_TAGS |
Custom build tags templates |
--files |
GORELEASER_FILES |
Additional files/template/globs you want to add to the archive |
--replacements |
GORELEASER_REPLACEMENTS |
Replacements for GOOS and GOARCH in the archive/binary name |
--envs |
GORELEASER_ENVS |
Custom environment variables to be set during the build |
--pre-hooks |
GORELEASER_PRE_HOOKS |
Hooks which will be executed before the build |
--post-hooks |
GORELEASER_POST_HOOKS |
Hooks which will be executed after the build |
--snapshot |
GORELEASER_SNAPSHOT |
Run in snapshot mode |
--checksum |
GORELEASER_CHECKSUM |
Create checksum (default true ) |
In order to use it, we will use the docker buildx
command in the following
examples. Buildx is a Docker component that
enables many powerful build features. All builds executed via buildx run with
Moby BuildKit builder engine.
Here is a minimal Dockerfile to build a Go project using goreleaser-xx
:
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM crazymax/goreleaser-xx:latest AS goreleaser-xx
FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS base
ENV CGO_ENABLED=0
COPY --from=goreleaser-xx / /
RUN apk add --no-cache git
WORKDIR /src
FROM base AS vendored
RUN --mount=type=bind,source=.,rw \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && go mod download
FROM vendored AS build
ARG TARGETPLATFORM
RUN --mount=type=bind,source=.,rw \
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/go/pkg/mod \
goreleaser-xx --debug \
--name="myapp" \
--dist="/out" \
--ldflags="-s -w -X 'main.version={{.Version}}'" \
--files="LICENSE" \
--files="README.md"
FROM scratch AS artifact
COPY --from=build /out/*.tar.gz /
COPY --from=build /out/*.zip /
FROM --platform=$BUILDPLATFORM ...
command will pull an image that willlinux/amd64
).BUILDPLATFORM
is part of the ARGs in the global scope.ARG TARGETPLATFORM
is also an ARG in the global scope that will be set--platform
flaggoreleaser-xx
will be able to automatically build againstMore details about multi-platform builds in this @tonistiigi/faster-multi-platform-builds-dockerfile-cross-compilation-guide-part-1-ec087c719eaf">blog post.
As you can see goreleaser-xx
CLI handles basic GoReleaser build customizations
with flags to be able to generate a temp and dynamic .goreleaser.yml
configuration,
but you can also include your own GoReleaser YAML config.
Let’s run a simple build against the artifact
target in our Dockerfile:
# build and output content of the artifact stage that contains the archive in ./dist
docker buildx build \
--output "./dist" \
--target "artifact" .
$ tree ./dist
./dist
├── myapp_v1.0.0-SNAPSHOT-00655a9_linux_amd64.tar.gz
└── myapp_v1.0.0-SNAPSHOT-00655a9_linux_amd64.tar.gz.sha256
Here linux/amd64
arch is used because it’s my current platform. If we want
to handle more platforms, we need to create a builder instance as building
multi-platform is currently only supported when using BuildKit with thedocker-container
or kubernetes
drivers.
# create a builder instance
$ docker buildx create --name "mybuilder" --use
# now build for other platforms
$ docker buildx build \
--platform "linux/amd64,linux/arm64,linux/arm/v7,windows/amd64,darwin/amd64" \
--output "./dist" \
--target "artifact" .
$ tree ./dist
./dist
├── darwin_amd64
│ ├── myapp_v1.0.0-SNAPSHOT-00655a9_darwin_x86_64.tar.gz
│ └── myapp_v1.0.0-SNAPSHOT-00655a9_darwin_x86_64.tar.gz.sha256
├── linux_amd64
│ ├── myapp_v1.0.0-SNAPSHOT-00655a9_linux_x86_64.tar.gz
│ └── myapp_v1.0.0-SNAPSHOT-00655a9_linux_x86_64.tar.gz.sha256
├── linux_arm64
│ ├── myapp_v1.0.0-SNAPSHOT-00655a9_linux_arm64.tar.gz
│ └── myapp_v1.0.0-SNAPSHOT-00655a9_linux_arm64.tar.gz.sha256
├── linux_arm_v7
│ ├── myapp_v1.0.0-SNAPSHOT-00655a9_linux_armv7.tar.gz
│ └── myapp_v1.0.0-SNAPSHOT-00655a9_linux_armv7.tar.gz.sha256
└── windows_amd64
│ ├── myapp_v1.0.0-SNAPSHOT-00655a9_windows_x86_64.tar.gz
│ └── myapp_v1.0.0-SNAPSHOT-00655a9_windows_x86_64.tar.gz.sha256
We can enhance the previous example to also create a multi-platform image in
addition to the generated artifacts:
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM crazymax/goreleaser-xx:latest AS goreleaser-xx
FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS base
ENV CGO_ENABLED=0
COPY --from=goreleaser-xx / /
RUN apk add --no-cache git
WORKDIR /src
FROM base AS vendored
RUN --mount=type=bind,source=.,rw \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && go mod download
FROM vendored AS build
ARG TARGETPLATFORM
RUN --mount=type=bind,source=.,rw \
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/go/pkg/mod \
goreleaser-xx --debug \
--name="myapp" \
--dist="/out" \
--ldflags="-s -w -X 'main.version={{.Version}}'" \
--files="LICENSE" \
--files="README.md"
FROM scratch AS artifact
COPY --from=build /out/*.tar.gz /
COPY --from=build /out/*.zip /
FROM alpine AS image
RUN apk --update --no-cache add ca-certificates openssl
COPY --from=build /usr/local/bin/myapp /usr/local/bin/myapp
EXPOSE 8080
ENTRYPOINT [ "myapp" ]
As you can see, we have added a new stage called image
. The artifact of each
platform is available with goreleaser-xx
in /usr/local/bin/{{ .ProjectName }}{{ .Ext }}
(build
stage) and will be included in your image
stage via COPY --from=build
command.
Now let’s build, tag and push our multi-platform image with buildx:
docker buildx build \
--tag "user/myapp:latest" \
--platform "linux/amd64,linux/arm64,linux/arm/v7" \
--target "image" \
--push .
windows/amd64
anddarwin/amd64
platforms have been removed here
becausealpine:3.14
does not support them.
.goreleaser.yml
You can also use a .goreleaser.yml
to configure your build:
env:
- GO111MODULE=auto
gomod:
proxy: true
builds:
- mod_timestamp: '{{ .CommitTimestamp }}'
flags:
- -trimpath
ldflags:
- -s -w
nfpms:
- file_name_template: '{{ .ConventionalFileName }}'
homepage: https://github.com/user/hello
description: Hello world
maintainer: Hello <hello@world.com>
license: MIT
vendor: HelloWorld
formats:
- apk
- deb
- rpm
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM crazymax/goreleaser-xx:latest AS goreleaser-xx
FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS base
ENV CGO_ENABLED=0
COPY --from=goreleaser-xx / /
RUN apk add --no-cache git
WORKDIR /src
FROM base AS vendored
RUN --mount=type=bind,source=.,rw \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && go mod download
FROM vendored AS build
ARG TARGETPLATFORM
RUN --mount=type=bind,source=.,rw \
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/go/pkg/mod \
goreleaser-xx --debug \
--config=".goreleaser.yml" \
--name="hello" \
--dist="/out" \
--main="." \
--files="README.md"
FROM scratch AS artifact
COPY --from=build /out/*.tar.gz /
COPY --from=build /out/*.zip /
Here are some examples to use CGO to build your project with goreleaser-xx
:
crazy-max/goxx
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM crazymax/goreleaser-xx:latest AS goreleaser-xx
FROM --platform=$BUILDPLATFORM crazymax/osxcross:11.3 AS osxcross
FROM --platform=$BUILDPLATFORM crazymax/goxx:1.17 AS base
COPY --from=osxcross /osxcross /osxcross
COPY --from=goreleaser-xx / /
ENV CGO_ENABLED=1
RUN goxx-apt-get install --no-install-recommends -y git
WORKDIR /src
FROM base AS vendored
RUN --mount=type=bind,source=.,rw \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && go mod download
FROM vendored AS build
ARG TARGETPLATFORM
RUN --mount=type=cache,sharing=private,target=/var/cache/apt \
--mount=type=cache,sharing=private,target=/var/lib/apt/lists \
goxx-apt-get install -y binutils gcc g++ pkg-config
RUN --mount=type=bind,source=.,rw \
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/go/pkg/mod \
goreleaser-xx --debug \
--name="myapp" \
--dist="/out" \
--ldflags="-s -w -X 'main.version={{.Version}}'" \
--files="LICENSE" \
--files="README.md"
FROM scratch AS artifact
COPY --from=build /out/*.tar.gz /
COPY --from=build /out/*.zip /
tonistiigi/xx
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM crazymax/goreleaser-xx:latest AS goreleaser-xx
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.1.2 AS xx
FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS base
ENV CGO_ENABLED=1
COPY --from=goreleaser-xx / /
COPY --from=xx / /
RUN apk add --no-cache clang git file lld llvm pkgconfig
WORKDIR /src
FROM base AS vendored
RUN --mount=type=bind,source=.,target=/src,rw \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && go mod download
FROM vendored AS build
ARG TARGETPLATFORM
RUN xx-apk add --no-cache gcc musl-dev
# XX_CC_PREFER_STATIC_LINKER prefers ld to lld in ppc64le and 386.
ENV XX_CC_PREFER_STATIC_LINKER=1
RUN --mount=type=bind,source=.,rw \
--mount=from=crazymax/osxcross:11.3,src=/osxsdk,target=/xx-sdk \
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/go/pkg/mod \
goreleaser-xx --debug \
--go-binary="xx-go" \
--name="myapp" \
--dist="/out" \
--ldflags="-s -w -X 'main.version={{.Version}}'" \
--files="LICENSE" \
--files="README.md"
FROM scratch AS artifact
COPY --from=build /out/*.tar.gz /
COPY --from=build /out/*.zip /
CGO_ENABLED
By default, CGO is enabled in Go when compiling for native architecture and
disabled when cross-compiling. It’s therefore recommended to always setCGO_ENABLED=0
or CGO_ENABLED=1
when cross-compiling depending on whether
you need to use CGO or not.
Everything is dockerized and handled by buildx bake for an
agnostic usage of this repo:
git clone https://github.com/crazy-max/goreleaser-xx.git goreleaser-xx
cd goreleaser-xx
# build docker image and output to docker with goreleaser-xx:local tag (default)
docker buildx bake
# build multi-platform image
docker buildx bake image-all
Want to contribute? Awesome! The most basic way to show your support is to star the project, or to raise issues. If
you want to open a pull request, please read the contributing guidelines.
You can also support this project by becoming a sponsor on GitHub or by
making a Paypal donation to ensure this journey continues indefinitely!
Thanks again for your support, it is much appreciated!
MIT. See LICENSE
for more details.