GoReleaser automates building, packaging, and publishing Go binaries. This article covers a complete configuration that adds cryptographic signing with Cosign and software bill of materials (SBOM) generation with Syft. Every artifact your project ships can be verified by consumers as authentic and unmodified.
This configuration pairs well with the verification pipeline in AI on a Leash for Go. The build-check target in that article’s Makefile validates the GoReleaser configuration locally before CI runs it for real.
Complete GoReleaser Configuration
Create .goreleaser.yml in your project root:
# .goreleaser.yml - Signed releases with SBOM
version: 2
before:
hooks:
- go mod tidy
- go generate ./...
builds:
- id: myproject
main: ./cmd/myproject
binary: myproject
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}
archives:
- format: tar.gz
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format_overrides:
- goos: windows
format: zip
checksum:
name_template: "checksums.txt"
signs:
- cmd: cosign
artifacts: checksum
args:
- "sign-blob"
- "--yes"
- "--output-signature=${signature}"
- "${artifact}"
sboms:
- artifacts: archive
cmd: syft
args: ["${artifact}", "--output", "spdx-json=${document}"]
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
release:
github:
owner: "{{ .Env.GITHUB_REPOSITORY_OWNER }}"
name: "{{ .ProjectName }}"
draft: false
prerelease: auto
Signing with Cosign
Cosign signs your release checksums using keyless signing (via Sigstore). Anyone can verify that the binary they downloaded was built from your CI pipeline, not injected by a compromised mirror or supply chain attack. The --yes flag enables non-interactive mode for CI.
Users can verify cryptographic proof that the binary came from your CI pipeline, not a compromised mirror.
SBOM Generation with Syft
Syft generates a Software Bill of Materials in SPDX JSON format for each archive. An SBOM lists every dependency baked into the binary: name, version, license. When a CVE drops against a library, consumers of your binary can check their SBOM to see if they’re affected without reading your source code.
SBOM generation is increasingly required for government and enterprise software procurement. Adding it now costs nothing.
Build Configuration Details
CGO_ENABLED=0 produces statically linked binaries. No glibc dependency, no shared library issues across Linux distributions. The binary runs anywhere.
ldflags: -s -w strips debug symbols and DWARF information, reducing binary size by 20-30%.
Version injection via -X main.version={{.Version}} embeds the git tag into the binary. Your binary can report its own version with myproject --version.
Cross-compilation to six targets (linux/darwin/windows times amd64/arm64) happens in a single command. Go’s cross-compilation support makes this trivial.
The before hooks run go mod tidy and go generate to ensure the build starts from a clean state. If go mod tidy changes go.sum, your commit was incomplete.
Version Injection in Practice
To use the injected version variables, add this to your main.go:
package main
import "fmt"
var (
version = "dev"
commit = "none"
date = "unknown"
)
func main() {
fmt.Printf("myproject %s (commit: %s, built: %s)\n", version, commit, date)
// ... rest of application
}
During development, these variables hold their default values. GoReleaser replaces them at build time with the actual git tag, commit hash, and build timestamp. This is the standard pattern for Go CLI tools, and the AI should follow it when generating new cmd/ entrypoints.
Validating Locally
Add a build-check target to your Makefile:
build-check:
go build ./...
go mod verify
goreleaser check
goreleaser release --snapshot --clean --skip=publish,sign,sbom
goreleaser check validates your configuration file. The snapshot build runs the full pipeline without publishing, so you catch broken build configs locally instead of in CI.
Installation
# GoReleaser
go install github.com/goreleaser/goreleaser/v2@latest
# Signing and SBOM (install via package manager or binary download)
# cosign: https://docs.sigstore.dev/cosign/system_config/installation/
# syft: https://github.com/anchore/syft#installation
Pin these to specific versions in your CI configuration. Using @latest is fine for local development, but CI should be reproducible.
This blog post, titled: "GoReleaser with Cosign Signing and Syft SBOM: Signed Builds and Supply Chain Security for Go Projects" by Craig Johnston, is licensed under a Creative Commons Attribution 4.0 International License.
