Release Process
February 28, 2026 · View on GitHub
This document describes the automated release process for floop using GoReleaser.
Overview
The release pipeline is fully automated with semantic version bumping:
- Code merges to
maintrigger automatic version detection from commit messages - Commits are parsed for conventional commit prefixes:
feat:→ minor,fix:/perf:→ patch, breaking changes → major - Chore-only merges (docs, ci, test, refactor, etc.) skip the release entirely
- GoReleaser builds binaries, publishes to GitHub Releases, and updates the Homebrew tap
Installation
Homebrew (macOS/Linux)
brew install nvandessel/tap/floop
Go
go install github.com/nvandessel/floop/cmd/floop@latest
Manual
Download binaries from GitHub Releases, extract, and add to your PATH.
Auto-Release
Every push to main that changes code files triggers auto-release.yml. Documentation-only changes (.md, docs/, .beads/, .floop/, LICENSE) and workflow changes (.github/) are ignored via paths-ignore. CI (Test, Lint, Build) is enforced as a required status check via GitHub rulesets, so it must pass before merge.
Semantic Version Bumping
Since the repo uses squash-merge, PR titles become commit messages. The pr-title.yml workflow enforces conventional commit format on PR titles. Version bumping uses svu (by the GoReleaser maintainer) to parse commits since the last tag and determine the bump type:
| Commit Type | Bump | Example |
|---|---|---|
feat!: or BREAKING CHANGE: in body | major | feat!: remove legacy API |
feat: | minor | feat: add --tags flag |
fix:, perf: | patch | fix: handle empty input |
chore:, ci:, test:, docs:, build:, style:, refactor: | skip | chore: update deps |
Skip Mechanisms
| Method | Use Case |
|---|---|
github-actions[bot] actor | Automatic — prevents version-bump commits from re-triggering |
[skip release] in commit message | Batch multiple PRs before releasing |
skip-release label on PR | Mark a PR as not release-worthy before merging |
Example: Batching Changes
When you want to merge several PRs before cutting a release:
- Add the
skip-releaselabel to each PR (or include[skip release]in merge commits) - Merge the PRs
- On the last PR, remove the label (or omit the skip marker) — the auto-release triggers
Release Artifacts
Each release produces:
| Artifact | Description |
|---|---|
| 6 binaries | linux/darwin/windows × amd64/arm64 |
| Archives | .tar.gz for Unix, .zip for Windows |
| Checksums | checksums.txt with SHA256 hashes |
| Release notes | Auto-generated by GoReleaser from conventional commits |
Archives include:
floopbinaryLICENSEREADME.mddocs/directory
Local Testing
Test the release process locally before pushing:
# Install tools (if not already installed)
go install github.com/goreleaser/goreleaser/v2@v2.14.1
go install github.com/caarlos0/svu/v3@latest
# Preview what svu would do
svu current # show current version
svu next # show next version based on commits
# Validate configuration
goreleaser check
# Test build without publishing
goreleaser build --snapshot --clean
# Check built binaries
ls -lh dist/*/
# Test version injection
./dist/floop_linux_amd64_v1/floop --version
# Test full release pipeline (doesn't publish)
goreleaser release --snapshot --clean
# Clean up
rm -rf dist/
Version Information
All binaries include build metadata:
./floop --version
# Output: floop version v0.2.0 (commit: abc1234, built: 2026-02-10T15:30:00Z)
./floop version --json
# Output: {"version":"v0.2.0","commit":"abc1234","date":"2026-02-10T15:30:00Z"}
Version sources:
version— From git tag (e.g.,v0.2.0)commit— Short git commit SHAdate— Build timestamp (RFC3339)
Development builds:
- Built with
make buildshowversion=dev - Include current commit SHA and build time
Troubleshooting
Release Workflow Fails
Check GoReleaser logs:
gh run view --log
Common issues:
- Missing tag: Ensure version bump workflow completed
- Build failure: Check
go.modand dependencies are up to date - Invalid config: Run
goreleaser checklocally
Wrong Version Tagged
If you need to delete and recreate a tag:
# Delete local tag
git tag -d v0.2.0
# Delete remote tag (careful!)
git push origin :refs/tags/v0.2.0
# Create new tag
git tag -a v0.2.0 -m "Release v0.2.0"
git push origin v0.2.0
Note: Only do this immediately after tagging, before anyone downloads the release.
Release Published but Broken
If a release is published but has issues:
- Don't delete the release — it breaks links
- Create a hotfix and release a new patch version
- Update the broken release description with a warning and link to the fix
Emergency Hotfix
For critical bugs in production:
# Create hotfix branch from the release tag
git checkout -b hotfix/critical-bug v0.2.0
# Fix the bug
# ... make changes ...
# Commit fix
git add .
git commit -m "fix: critical bug description"
# Push hotfix branch
git push origin hotfix/critical-bug
# Create PR to main
gh pr create --base main --title "fix: critical bug" --body "Emergency hotfix for v0.2.0"
# After PR merge, auto-release creates the patch automatically
CI/CD Workflows
pr-title.yml
Trigger: Pull request opened, edited, or synchronized Purpose: Enforce conventional commit format on PR titles (since squash-merge uses PR title as commit message)
Steps:
- Validate PR title against allowed conventional commit types using
amannn/action-semantic-pull-request
auto-release.yml
Trigger: Push to main (code changes only, docs excluded)
Purpose: Automatically release with semantic version bumping from commit messages
Jobs:
check-skip— Evaluate skip conditions (bot actor, commit message, PR label)release— Usesvu nextto determine version, tag, and run GoReleaser
test-release.yml
Trigger: PR changes to release files
Permissions: contents: read
Purpose: Validate release config before merge
Steps:
- Checkout code
- Run GoReleaser in snapshot mode
- Verify binaries work
- Check for expected builds
Configuration Files
| File | Purpose |
|---|---|
.goreleaser.yml | GoReleaser configuration (builds, archives, changelog, Homebrew cask) |
.github/workflows/pr-title.yml | Conventional commit enforcement on PR titles |
.github/workflows/auto-release.yml | Auto-release on merge to main (svu + GoReleaser) |
.github/workflows/test-release.yml | PR validation workflow |
Makefile | Local build with version injection |
Homebrew Distribution
Each release automatically pushes a Homebrew cask to nvandessel/homebrew-tap via GoReleaser's homebrew_casks configuration.
How it works:
- GoReleaser builds archives for all platforms
- The
homebrew_caskssection creates/updates a cask file in the tap repo - Users install with
brew install nvandessel/tap/floop
Requirements:
HOMEBREW_TAP_GITHUB_TOKENsecret on the floop repo (PAT with repo scope to push tohomebrew-tap)- The
nvandessel/homebrew-taprepository must exist
Future Enhancements
Not currently implemented but can be added later:
- Docker images — Multi-arch container publishing
- Scoop manifest — Windows package manager
- AUR package — Arch Linux user repository
- Binary signing — GPG or cosign signatures
- SBOM generation — Software bill of materials
- Release channels — Beta/RC releases
To add these, extend .goreleaser.yml with the appropriate sections. See GoReleaser documentation for details.
Questions?
For questions about the release process:
- Check the GoReleaser docs
- Review past releases:
gh release list - Open an issue with the
questionlabel