go-release · diff

git:20260610.3a4cd11 to git:20260701.20789e5

1 added, 1 removed. Audit A to A.

---
name: go-release
- description: Release engineering for Go projects — GoReleaser v2 with version ldflags into an internal/version package, SBOMs and multi-arch container images, tag-triggered GitHub Actions releases, CI running build/vet/test -race/govulncheck with SHA-pinned actions, and Dependabot coverage for gomod, tools, npm, and Actions. Use when setting up releases or GoReleaser for a Go project, writing GitHub Actions CI or release workflows for a Go repository, stamping version metadata into a Go binary at build time, or adding Dependabot coverage to a Go repo.
+ description: Release engineering for Go projects — GoReleaser v2 with version ldflags into an internal/version package, SBOMs and multi-arch container images, tag-triggered GitHub Actions releases, CI running build/vet/test -race/govulncheck with SHA-pinned actions, and Dependabot coverage for gomod, tools, npm, and Actions. Use when adding GitHub Actions CI for a Go repository; writing a release workflow that publishes Go binaries, SBOMs, and multi-arch container images; setting up GoReleaser for a Go project; stamping version metadata into a Go binary at build time; or adding Dependabot coverage to a Go repo. Not for scaffolding a new Go project or its Makefile.
license: MIT
---
# Go release engineering
Tag-driven releases: pushing a `vX.Y.Z` tag builds cross-platform archives, checksums, SBOMs, and
multi-arch container images via GoReleaser. CI gates every push; Dependabot keeps everything —
including the action pins — fresh. This layers on the layout and Makefile from the `go-project`
skill.
## 1. Stamp version metadata into `internal/version`
```go
// Package version exposes build metadata stamped into the binary at link time.
// Keep these as vars (not consts) so -ldflags "-X ..." can rewrite them; an
// unbuilt or `go run` binary reports the defaults.
package version
var (
Version = "dev" // semver release tag, e.g. v1.2.3
Commit = "none" // short git SHA
BuildDate = "unknown" // RFC3339 UTC build timestamp
)
```
Both `make build` and GoReleaser inject the same vars at the same import path:
```text
-ldflags "-s -w -X <module>/internal/version.Version={{ .Version }} ..."
```
Expose the metadata through `--version` output or a `GET /version` endpoint.
## 2. GoReleaser config
Copy [templates/goreleaser.yaml](templates/goreleaser.yaml) to `.goreleaser.yaml` and replace the
`myapp` / `OWNER/REPO` placeholders. What it encodes:
- Static, reproducible binaries: `CGO_ENABLED=0`, `-trimpath`, `-s -w`, for linux + darwin ×
amd64 + arm64.
- `tar.gz` archives shipping the `LICENSE`, a `checksums.txt`, and an SBOM per archive —
generated by the `syft` pinned in `tools/go.mod` and invoked via `go tool`, so nothing needs to
be on `PATH`.
- A changelog that excludes `docs:` / `test:` / `chore:` / `ci:` conventional-commit prefixes.
- `dockers_v2` multi-arch images (buildx) pushed to GHCR with OCI annotations.
Validate locally with `make snapshot` (`goreleaser release --snapshot --clean`) — full build, no
publish.
## 3. CI workflow
Copy [templates/ci.yaml](templates/ci.yaml) to `.github/workflows/ci.yaml`. Every push and pull
request runs `go build`, `go vet`, `go test -race`, and `govulncheck`. Conventions:
- The Go version comes from `go.mod` (`go-version-file`), never hard-coded in the workflow.
- Every action is pinned to a full commit SHA with the tag in a trailing comment — a moved tag
can never change what runs. Dependabot keeps the pins fresh.
- `permissions: contents: read` — the default token does nothing else.
## 4. Release workflow
Copy [templates/release.yaml](templates/release.yaml) to `.github/workflows/release.yaml`. It
triggers on `v*` tags, checks out with `fetch-depth: 0` (GoReleaser needs full history for the
changelog), sets up QEMU + buildx for multi-arch images, logs into GHCR with the built-in
`GITHUB_TOKEN`, and runs `goreleaser release --clean` — SBOMs come from the pinned `syft` via
`go tool`, so there is no separate syft install step. Cutting a release is exactly: push a semver
tag.
## 5. Dependabot
Copy [templates/dependabot.yaml](templates/dependabot.yaml) to `.github/dependabot.yaml`: daily
checks with a 7-day cooldown, minor + patch bumps grouped into one PR per ecosystem (majors arrive
as individual PRs). `tools/go.mod` gets its own `gomod` entry — Dependabot does not descend into
nested modules — so developer-tool bumps never ride along with application dependency PRs, and the
`github-actions` entry keeps the workflow SHA pins fresh. For the rationale behind each knob and
the full ecosystem matrix (`docker`, `docker-compose`, `uv`, …), see [reference.md](reference.md).