Building GoReleaser: from shell script to paid product
In this post, I want to share the history behind GoReleaser, how we got here, lessons …
This version introduces the new version of the Docker integration, Docker image attestation, Makeself packaging support, Go 1.25, and much more!
The dockers
feature in GoReleaser is rather old:
it’s from a time before multi-architecture Docker images (manifests).
Its configuration is also very verbose and repetitive.
These were problems we were aiming to solve for a while, and after a lot of discussion, we arrived at what I think is a good solution.
It’ll be in alpha stage for a while, and you can use it as dockers_v2
in your
configuration.
Once we’re set on the API, we’ll deprecate dockers
and docker_manifests
.
Eventually, when we launch GoReleaser v3, the old features will be removed, and
dockers_v2
will simply be dockers
.
That all being said, let’s dive in!
Basically, we’ll always use docker buildx
to build the manifest.
This means:
dockers
+ docker_manifests
wiringDepending on how familiar you are with Docker manifests, you may be asking “how
does this work when I do a local build with --snapshot
?” - and that’s a great
question!
Manifests require us to push the images, so, to avoid doing that in snapshot
builds, when the build is a snapshot, GoReleaser will automatically “explode”
the build configuration by platform, and build local images with the
architecture in the tag.
This allows you to still test both the build process and correctness of the
images without having to push any images.
A simple configuration would look like this:
# .goreleaser.yaml
dockers_v2:
- images:
- user/repo
tags:
- "{{.Version}}"
- latest
platform:
- linux/amd64
- linux/arm64
And the Dockerfile
might look like so:
# Dockerfile
FROM scratch
ARG TARGETPLATFORM
ENTRYPOINT ["/usr/bin/myprogram"]
COPY $TARGETPLATFORM/myprogram /usr/bin/
You can add however many images and tags you want, and GoReleaser will build the
combination of all of them.
You can also set annotations
, labels
, Docker flags, etc.
Read the documentation for more information.
Makeself allows to create packages that self-extract/run. GoReleaser now supports creating them.
You basically only need to set the script that it’ll run after extracting your binary.
Here’s an example:
# .goreleaser.yaml
makeselfs:
- script: ./run.sh
Your shell script can simply be:
#!/bin/sh
# run.sh
./myprogram $*
And it should work. Read the documentation for more information.
You can now also more easily attest your Docker images within GitHub actions.
Basically, GoReleaser will now automatically generate ./dist/digests.txt
file,
which you can then pass to the attest action:
# ./.github/workflows/release.yml
# ...
permissions:
# ...
# Give the workflow permission to write attestations.
id-token: write
attestations: write
jobs:
release:
# ...
steps:
# ...
- uses: goreleaser/goreleaser-action@v6
with:
# ...
# After GoReleaser runs, attest all the files in ./dist/checksums.txt:
- uses: actions/attest-build-provenance@v2
with:
subject-checksums: ./dist/checksums.txt
# After GoReleaser runs, attest all the images in ./dist/digests.txt:
- uses: actions/attest-build-provenance@v2
with:
subject-checksums: ./dist/digests.txt
This will create 2 attestations, one for binaries/archives/etc and another one for your Docker images.
Read the documentation for more information.
Thanks to Brian DeHamer from the GitHub team for the help making this happen!
If you do constant pre-releases, this is for you!
Imagine you have a history like this:
v0.1.0
v0.2.0-beta.1
v0.2.0-beta.2
v0.2.0-beta.3
v0.2.0
And you want to release v0.2.0
.
Usually, GoReleaser would get v0.2.0-beta.3
as previous version, but that’s
likely not what most people would expect (v0.1.0
).
Smart semver will ignore pre-release versions in these cases, making the release notes more complete.
To use it, add this to your configuration:
# .goreleaser.yaml
git:
tag_sort: smartsemver
If you were to release v0.2.0-beta.3
, though, it would still get
v0.2.0-beta.2
as previous version, which I think makes sense.
See the documentation for more details.
nightly.draft
)commit_author
can now be set globally in metadata
readFile
and mustReadFile
password
username
&&
instead of and
conflicts_with
(deprecated by Homebrew)You can install or upgrade using your favorite package manager, or see the full release notes and download the pre-compiled binaries from GitHub:
You can help by reporting issues, contributing features, documentation improvements, and bug fixes. You can also sponsor the project, or get a GoReleaser Pro license.