Home

Awesome

goreleaser-xx

GitHub release Build Status Docker Stars Docker Pulls Go Report Card

⚠️ Abandoned project

This project is not maintained anymore and is abandoned. Feel free to fork and make your own changes if needed.

About

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


Features

Image

RegistryImage
Docker Hubcrazymax/goreleaser-xx
GitHub Container Registryghcr.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

CLI

docker run --rm -t crazymax/goreleaser-xx:latest goreleaser-xx --help
FlagEnv varDescription
--debugDEBUGEnable debug (default false)
--git-refGIT_REFThe branch or tag like refs/tags/v1.0.0 (default to your working tree info)
--goreleaserGORELEASER_PATHSet a specific GoReleaser binary to use (default goreleaser)
--configGORELEASER_CONFIGLoad GoReleaser configuration from file
--go-binaryGORELEASER_GOBINARYSet a specific go binary to use when building (default go)
--nameGORELEASER_NAMEProject name
--distGORELEASER_DISTDist folder where artifact will be stored
--artifactsGORELEASER_ARTIFACTSTypes of artifact to create (archive, bin) (default archive)
--mainGORELEASER_MAINPath to main.go file or main package (default .)
--flagsGORELEASER_FLAGSCustom flags templates
--asmflagsGORELEASER_ASMFLAGSCustom asmflags templates
--gcflagsGORELEASER_GCFLAGSCustom gcflags templates
--ldflagsGORELEASER_LDFLAGSCustom ldflags templates
--tagsGORELEASER_TAGSCustom build tags templates
--filesGORELEASER_FILESAdditional files/template/globs you want to add to the archive
--replacementsGORELEASER_REPLACEMENTSReplacements for GOOS and GOARCH in the archive/binary name
--envsGORELEASER_ENVSCustom environment variables to be set during the build
--pre-hooksGORELEASER_PRE_HOOKSHooks which will be executed before the build
--post-hooksGORELEASER_POST_HOOKSHooks which will be executed after the build
--snapshotGORELEASER_SNAPSHOTRun in snapshot mode
--checksumGORELEASER_CHECKSUMCreate checksum (default true)

Usage

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.

Minimal

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 /

More details about multi-platform builds in this 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 the docker-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

Multi-platform image

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 and darwin/amd64 platforms have been removed here because alpine:3.14 does not support them.

With .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 /

CGO

Here are some examples to use CGO to build your project with goreleaser-xx:

crazy-max/goxx

https://github.com/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

https://github.com/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 /

Notes

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 set CGO_ENABLED=0 or CGO_ENABLED=1 when cross-compiling depending on whether you need to use CGO or not.

Build

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

Contributing

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! :pray:

License

MIT. See LICENSE for more details.