Releasing a New Version
Prerequisites
- •Go installed matching go.mod version
- •Clean working tree (
git statusshows no changes) - •All tests passing (
go test ./...) - •GitHub CLI authenticated (
gh auth status) - •No
replacedirectives in go.mod - •GoReleaser configured (
.goreleaser.ymlin repo root) - •
HOMEBREW_TAP_TOKENsecret exists in GitHub repo settings
Beware of go.work: A parent go.work file can silently use local dependencies instead of published versions. Always use GOWORK=off when updating dependencies and testing builds.
Release Process
1. Determine Version
Follow semantic versioning:
- •Major (v2.0.0): Breaking changes
- •Minor (v0.2.0): New features, backward compatible
- •Patch (v0.1.1): Bug fixes only
git tag -l | sort -V | tail -1
2. Update td Dependency
Sidecar embeds td as a Go module. Always update to latest before releasing:
GOWORK=off go get github.com/marcus/td@latest GOWORK=off go mod tidy
3. Verify go.mod
Ensure no replace directives (they break go install):
grep replace go.mod && echo "ERROR: Remove replace directives before releasing!" && exit 1
4. Verify Build Without go.work
GOWORK=off go build ./...
If this fails with "undefined" errors, the required dependency version hasn't been published yet.
5. Update CHANGELOG.md
## [vX.Y.Z] - YYYY-MM-DD ### Features - New feature description ### Bug Fixes - Fix description ### Dependencies - Dependency update description
git add CHANGELOG.md git commit -m "docs: Update changelog for vX.Y.Z"
6. Create and Push Tag
git tag vX.Y.Z -m "Brief description of release" git push origin main && git push origin vX.Y.Z
7. GitHub Release (Automated)
Pushing the tag triggers GitHub Actions GoReleaser, which:
- •Creates the GitHub Release with changelog
- •Builds and attaches binaries for darwin/linux (amd64/arm64)
- •Generates checksums
- •Updates the Homebrew tap (
marcus/tap/sidecar)
8. Verify
# Check workflow succeeded gh run list --workflow=release.yml --limit=1 # Check release exists with binaries gh release view vX.Y.Z # Test Homebrew install brew install marcus/tap/sidecar sidecar --version # Test go install (critical!) GOWORK=off go install github.com/marcus/sidecar/cmd/sidecar@vX.Y.Z sidecar --version # Should output: sidecar version vX.Y.Z # Test update notification go build -ldflags "-X main.Version=v0.0.1" -o /tmp/sidecar-test ./cmd/sidecar /tmp/sidecar-test # Should show toast: "Update vX.Y.Z available!"
Version in Binaries
Version is embedded at build time via ldflags:
go build -ldflags "-X main.Version=v0.2.0" ./cmd/sidecar go install -ldflags "-X main.Version=v0.2.0" ./cmd/sidecar
Without ldflags, version falls back to:
- •Go module version (if installed via
go install) - •Git revision (
devel+abc123) - •
devel
Update Mechanism
On startup, sidecar checks https://api.github.com/repos/marcus/sidecar/releases/latest, compares tag_name against current version, and shows a toast if newer. Results cached for 3 hours. Pre-release suffixes (-rc1, -beta) are stripped for comparison. Dev versions skip the check.
Recovery: Fixing a Bad Release
- •Publish a new patch release with fixes
- •For critical bugs, release immediately
- •Delete unpublished GitHub release:
gh release delete vX.Y.Z - •Keep git tags to preserve history
- •If GoReleaser workflow fails, re-run locally:
goreleaser release --clean
Install Methods
- •Setup script:
curl -fsSL https://raw.githubusercontent.com/marcus/sidecar/main/setup.sh | bash - •Homebrew:
brew install marcus/tap/sidecar - •Download binary: from GitHub Releases page
- •From source:
go install github.com/marcus/sidecar/cmd/sidecar@latest
Checklist
- • Tests pass
- • Working tree clean
- • td dependency updated (
GOWORK=off go get github.com/marcus/td@latest) - • No
replacedirectives in go.mod - • Build works without go.work (
GOWORK=off go build ./...) - • CHANGELOG.md updated
- • Version follows semver
- • Tag created and pushed
- • GitHub Actions workflow completed
- • Binaries attached to release
- • Homebrew tap updated
- • Installation verified (
GOWORK=off go install ...@vX.Y.Z) - • Update notification verified