| name | releasing-software |
| description | Use when preparing a release, tagging a version, or when user says "release", "tag", "ship it", "push to production" - ensures all release artifacts are verified before tagging to avoid the retag-four-times failure pattern |
Releasing Software
Overview
Never tag until CI passes. Every retag erodes trust and wastes time.
This skill prevents the "tag, watch CI fail, retag, repeat" anti-pattern by establishing a pre-flight checklist that catches issues BEFORE creating the tag.
The Iron Law
NO TAG WITHOUT GREEN CI
Run the full verification locally. Fix everything. THEN tag. Not before.
Pre-Release Checklist
MANDATORY: Use TodoWrite to create todos for EACH item.
1. Verify Build Paths
Check ALL places that reference your main package:
grep -r "cmd/" . --include="*.yml" --include="*.yaml" --include="Makefile" --include="Dockerfile"
grep -r "go build" . --include="*.yml" --include="*.yaml" --include="Makefile"
2. Verify Tests Exist
Go 1.23+ coverage tool fails on packages without tests:
find . -name "*_test.go" | wc -l
Every package needs at least one test file or CI coverage step fails with no such tool "covdata".
3. Verify Local CI Passes
make test
make lint
make build
make test-coverage
4. Verify Documentation
5. Verify Release Config
6. Verify Clean Git State
git status
git diff --stat
git log --oneline -5
Release Procedure
Only after ALL checks pass:
git add -A && git commit -m "release: prepare v0.0.X"
git push origin main
gh run list --limit 2
git tag -a v0.0.X -m "v0.0.X"
git push origin v0.0.X
gh run list --limit 2
Red Flags - STOP
If you catch yourself doing any of these, STOP:
- Tagging before CI completes
- "CI will probably pass"
- "I'll fix it if it fails"
- Deleting and recreating tags
- Force-pushing tags
- "It's just a minor release"
All of these mean: Wait for CI. Fix issues. Then tag.
Common Failures
| Symptom | Cause | Fix |
|---|
couldn't find main file | Wrong path in goreleaser | Set main: . if main.go at root |
no such tool "covdata" | Package without tests | Add placeholder test to each package |
| Release succeeds but brew fails | Wrong description/homepage | Update goreleaser brews section |
| Had to retag | Tagged before CI passed | WAIT FOR GREEN CI |
| 504 errors in CI | GitHub infra issue | Retry workflow, not a code problem |
Version Bumping
When incrementing version:
- Patch (0.0.X): Bug fixes, no new features
- Minor (0.X.0): New features, backwards compatible
- Major (X.0.0): Breaking changes
Search codebase for version references:
grep -r "version" . --include="*.go" --include="*.yml" --include="*.json" | grep -v test | grep -v vendor
Post-Release Verification
After tag is pushed:
gh run watch
gh release view v0.0.X
go install github.com/user/repo@v0.0.X
The Bottom Line
Retagging is a symptom of rushing.
The 5 minutes spent on pre-flight checks saves 30 minutes of retag cycles and preserves your release history integrity.
Every tag should be final. If you're deleting tags, you skipped the checklist.