feat(release): automate releases on tag push - binaries, image, brew - #39
Merged
Conversation
Why: releases were a manual goreleaser run on Amy's laptop; kaibo already has a tag-triggered, signed, attested pipeline and otel-cli should flow the same. Approach: keep goreleaser for binaries/archives/nfpm/checksums/SBOM/cosign bundle/GitHub release/Homebrew formula; build the ghcr image in the workflow from dist/artifacts.json binaries so it can be pushed by digest, signed, attested, and tagged LAST (goreleaser's docker pipeline signs after tagging, leaving a signature artifact as the package page's "latest"). SHA-pinned actions, dependabot, CI snapshot gate, release page header template. Learned: goreleaser v2 deprecates `brews` for `homebrew_casks`, but casks are macOS-only and the tap serves Linux, so the formula stays and `goreleaser check` is non-zero by design. Snapshot build + actionlint pass locally. Next: kaibo review; Amy creates HOMEBREW_TAP_GITHUB_TOKEN secret and makes the ghcr package public; workflow_dispatch smoke; then tag v0.6.0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lish Why: kaibo review (crusoe cast: GLM-5.3 synth, DeepSeek-V4-Flash explorer) found that `gh workflow run release.yml --ref v0.6.0` is a dispatch whose ref_type is 'tag', so every step gated on ref_type alone would publish for real from a hand-triggered run. Approach: gate on `github.event_name == 'push' && github.ref_type == 'tag'` everywhere; add a go build + `version` smoke before GoReleaser so a crash-at-startup binary fails before the GitHub release and tap push exist; jq first() so a second build id can't split $bin; sort -u the image tags (prerelease semver patterns collapse to duplicates). Docs: README Alpine note no longer says "latest", BOTS.md points at the tag-push flow, dev Dockerfile pins golang:1.26 so Dependabot can bump it. Learned: the review's other two GoReleaser findings (`sboms.artifacts: any` invalid, `$document` unsubstituted) were false — verified against the v2.18 JSON schema and a snapshot run with a syft shim: exactly one SBOM, listed in checksums.txt. The CI snapshot does render the formula under --skip=publish. Next: push, PR, Amy sets HOMEBREW_TAP_GITHUB_TOKEN + ghcr visibility, dispatch smoke, then a -rc tag before the stable one. 🤖 Reviewed by kaibo (crusoe: zai-org/GLM-5.3 + deepseek-ai/Deepseek-V4-Flash) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Why: the packaging metadata carried tobert@github.com, an address that existed once long ago; Amy's email has been tobert@gmail.com for decades. Approach: one-line change in the nfpms block; commit_author already used the right address. Next: nothing; ships with the release automation PR. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Why: PR #39 was unmergeable: the branch ruleset on main requires a status check named `build`, and renaming the job to `test` meant it never reported. Approach: rename the job id back and leave a comment so the next rename doesn't repeat this. The goreleaser snapshot job is not required by the ruleset; adding it is a repo-settings change for Amy. Next: merge #39. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
What
Releases are now cut by GitHub Actions on a
v*tag push, ported from kaibo's pipeline and adapted to a Go project that already had goreleaser..github/workflows/release.yml(new): goreleaser builds binaries, archives, apk/deb/rpm, checksums, an SPDX SBOM (syft over go.mod), cosign keyless-signschecksums.txtas a.sigstore.jsonbundle, publishes the GitHub release and pushes the Homebrew formula. The workflow then stages the linux amd64/arm64 binaries fromdist/artifacts.json, builds a multiarch alpine image, pushes by digest, signs, attests SLSA provenance, and applies the version tags last so the ghcr package page's "latest" is always a pullable image.workflow_dispatchruns the whole thing as a snapshot that publishes nothing (gated on the event, not the ref, so a dispatch against a tag ref stays a snapshot)..goreleaser.yml: docker sections removed (the workflow owns the image), SBOM + cosign +github-nativechangelog + release header template added,make_latestoff for prereleases,replace_existing_artifactsfor re-runs..github/workflows/ci.yml: SHA-pinned actions,contents: read, gofmt/vet/tidy gate, plus a goreleaser snapshot job so the release config can't rot between releases..github/dependabot.yml: actions, gomod, docker.release/Dockerfile: digest-pinned Alpine 3.24,COPY binaries/${TARGETARCH}/otel-cli..github/release-body.md: per-release "Get it / Verify it" header with the tag filled in.brew install tobert/otel-cli/otel-cli, a "Verify a download" section, a rewritten "Releases" section. BOTS.md and CHANGELOG updated.Decisions worth a look
brewsin favour ofhomebrew_casks, but casks are macOS-only and the tap serves Linux; a cask would also make every currentbrew install otel-cliuser reinstall. Kept the formula, sogoreleaser checkexits non-zero by design and CI validates with a snapshot instead. Tracked in Homebrew: goreleaser deprecatesbrews(formula) in favour ofhomebrew_casks#40.Formula/otel-cli.rbin the tap; the old roototel-cli.rbbecomes dead onceFormula/exists and can be deleted after the first release.v0.6.0(continuity with 0.5.0) +0.6.0+0.6+latest. Still Alpine, still root, as the README documents; nonroot is a follow-up.Before the first tagged release
HOMEBREW_TAP_GITHUB_TOKEN: fine-grained PAT, Contents read/write ontobert/homebrew-otel-clionly. The workflow refuses to start a tag run without it.ghcr.io/tobert/otel-cliis currently private; flip it to public in the package settings ordocker pullfails for everyone.release.ymlby hand once (snapshot) and cut av0.6.0-rc.1beforev0.6.0: the rc exercises the release header, attestations and image tag policy without touching the tap orlatest.Verification
goreleaser check: valid (only the deliberatebrewsdeprecation)goreleaser release --snapshot --clean --skip=publish,sign,sbom: full matrix, formula rendered, linux/amd64 binary static andversionrunschecksums.txt🤖 Reviewed by kaibo (crusoe: zai-org/GLM-5.3 synth + deepseek-ai/Deepseek-V4-Flash explorer)
🤖 Generated with Claude Code