Publish Docker images on merge to main, not just on tags - #27
Merged
Conversation
The image was only built on tag pushes, so there was nothing to pull between releases. Merges to main now publish latest, and cross-compiling replaces QEMU emulation so building four platforms per merge is viable.
A RUN in this stage executes on the target platform, which is exactly the QEMU emulation the cross-compiling builder eliminated -- and the workflow no longer registers binfmt handlers, so the arm stages would have failed outright. The certificate bundle it installed ships with the base image (ca-certificates-bundle) since Alpine 3.9. The base also moves to the Alpine release the builder already uses.
GoReleaser stamps release binaries without the prefix and the image tags are unprefixed semver, so a tag build would have reported v1.2.3 while everything around it says 1.2.3.
rethab
added a commit
that referenced
this pull request
Aug 5, 2026
The fork inherited upstream's Docker workflow, which publishes to `ghcr.io` only when a release is cut. Between releases there was nothing to pull, even though every merge to `main` already produces a snapshot release and a Homebrew snapshot cask. Merges to `main` now publish an image too. `latest` follows `main`, and releases publish version tags (`1.2.3`, `1.2`). That is the opposite of the Docker default, where `latest` means "newest release", so it is called out in the README: pin a version tag if you want a stable image. Merges additionally get an immutable `main-<sha>` tag. Building four platforms on every merge is only affordable if the build is fast, so the Dockerfile now cross-compiles with Go from the build platform instead of running anything under QEMU emulation. The runtime stage is kept free of `RUN` instructions to make that complete — its `apk` install of ca-certificates was redundant anyway, since Alpine ships the certificate bundle in the base image since 3.9 — so nothing ever executes on the target platform and the workflow needs no QEMU/binfmt setup at all. Cross-compiling meant swapping `go install` for `go build`, since `go install` refuses to cross-compile into `GOBIN`; the linker flags still come from the Makefile (new `ldflags` target), with the tag's `v` prefix stripped, so the version stamped into an image matches a release binary (`1.2.3`, not `v1.2.3`). Smaller things that fall out of that: - `linux/arm/v8` is dropped from the platform list. No client asks for it — ARMv8 hardware reports `linux/arm64`, which we already build — so it was a redundant entry in the manifest. - The runtime base moves from `alpine:3.19` to `3.23`, the release the builder already uses. - The image now carries a signed provenance attestation, matching what the release and snapshot workflows already do for their artifacts. There is deliberately no build cache: a cache that any branch can write to is a way into the images we publish, which is the same reasoning that already keeps the Go cache off the release builds.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The fork inherited upstream's Docker workflow, which publishes to
ghcr.ioonly when a release is cut. Between releases there was nothing to pull, even though every merge tomainalready produces a snapshot release and a Homebrew snapshot cask. Merges tomainnow publish an image too.latestfollowsmain, and releases publish version tags (1.2.3,1.2). That is the opposite of the Docker default, wherelatestmeans "newest release", so it is called out in the README: pin a version tag if you want a stable image. Merges additionally get an immutablemain-<sha>tag.Building four platforms on every merge is only affordable if the build is fast, so the Dockerfile now cross-compiles with Go from the build platform instead of running anything under QEMU emulation. The runtime stage is kept free of
RUNinstructions to make that complete — itsapkinstall of ca-certificates was redundant anyway, since Alpine ships the certificate bundle in the base image since 3.9 — so nothing ever executes on the target platform and the workflow needs no QEMU/binfmt setup at all. Cross-compiling meant swappinggo installforgo build, sincego installrefuses to cross-compile intoGOBIN; the linker flags still come from the Makefile (newldflagstarget), with the tag'svprefix stripped, so the version stamped into an image matches a release binary (1.2.3, notv1.2.3).Smaller things that fall out of that:
linux/arm/v8is dropped from the platform list. No client asks for it — ARMv8 hardware reportslinux/arm64, which we already build — so it was a redundant entry in the manifest.alpine:3.19to3.23, the release the builder already uses.