This document describes the complete release process for stackctl.
# Check what's changed
make version
# Test build locally
make release-snapshot
# Prepare release
make changelog-prepare
# (Then manually edit CHANGELOG.md)
# Create and push release
git add CHANGELOG.md
git commit -m "chore: prepare release vX.Y.Z"
git tag -a vX.Y.Z -m "Release vX.Y.Z"
git push && git push --tagsstackctl uses a semi-automated release process:
- Manual: Update CHANGELOG.md with human-friendly release notes
- Automatic: GitHub Actions builds binaries and publishes release
- Automatic: GoReleaser generates additional notes from git commits
This gives us both:
- Rich, curated release notes (CHANGELOG.md)
- Automated binary distribution (GoReleaser + GitHub Actions)
We follow Semantic Versioning 2.0.0:
- MAJOR (X.0.0): Breaking changes, incompatible API changes
- MINOR (0.X.0): New features, backward compatible
- PATCH (0.0.X): Bug fixes, backward compatible
Use suffixes for pre-releases:
- Release Candidate:
v0.1.0-rc1,v0.1.0-rc2 - Beta:
v0.2.0-beta1 - Alpha:
v0.3.0-alpha1
Pre-releases are marked as "Pre-release" on GitHub.
# Check version status
make version
# Verify working tree is clean
git status
# Run tests
make test
# Test local build
make release-snapshotExpected Output:
- Latest tag displayed
- Unreleased changes shown
- Commits since last tag listed
- Suggested next version
Run the helper script:
make changelog-prepareThis will:
- Prompt for version number (e.g.,
0.1.0) - Update CHANGELOG.md with version and date
- Create a new
[Unreleased]section
Manual Steps:
- Review the generated changes in
CHANGELOG.md - Move items from
Unreleasedto the new version section - Organize items by category:
- Added: New features
- Changed: Changes to existing functionality
- Deprecated: Soon-to-be removed features
- Removed: Removed features
- Fixed: Bug fixes
- Security: Security fixes
Example:
## [Unreleased]
### Planned
- Package refactoring
- CLI feature parity
## [0.1.0] - 2026-03-10
### Added
- Binary distribution system with GoReleaser
- Embedded templates using `go:embed`
- New installer script for pre-built binaries
### Changed
- Migrated from external template files to embedded FS
### Fixed
- Template rendering issues with special charactersgit add CHANGELOG.md
git commit -m "chore: prepare release v0.1.0"Commit Message Convention:
- Use
chore: prepare release vX.Y.Zformat - This commit will NOT be included in the release changelog (filtered by GoReleaser)
# Create annotated tag
git tag -a v0.1.0 -m "Release v0.1.0"
# Verify tag
git tag -l -n9 v0.1.0Important:
- Always use annotated tags (
-aflag) - Tag message should be brief: "Release vX.Y.Z"
- GoReleaser requires annotated tags
# Push commit
git push
# Push tag (triggers release)
git push --tagsWhat Happens Next:
- GitHub Actions detects the
v*tag - Workflow
.github/workflows/release.ymlstarts - GoReleaser builds binaries for linux/amd64 and linux/arm64
- GitHub Release is created with:
- Binaries as downloadable assets
- Auto-generated changelog from commits
- Link to CHANGELOG.md for full release notes
- Installation instructions
After 5-10 minutes:
-
Check GitHub Actions: Ensure workflow succeeded
https://github.com/imran110219/stackctl/actions -
Check GitHub Releases: Verify release is published
https://github.com/imran110219/stackctl/releases -
Test Installation: On a clean VM
curl -fsSL https://raw.githubusercontent.com/imran110219/stackctl/main/install.sh | bash stackctl version
After successful release:
# Update ROADMAP.md
# Mark completed tasks as ✅ Done
# Update MEMORY.md if needed
# Add release to "Release Tracking" section
# Commit updates
git add docs/ROADMAP.md
git commit -m "docs: update roadmap after v0.1.0 release"
git pushmake build # Build binary
make build-dev # Build with dev version info
make install # Install to /usr/local/bin
make test # Run tests
make clean # Remove build artifactsmake version # Show current version status
make changelog-prepare # Prepare CHANGELOG for release
make release-check # Validate GoReleaser config
make release-snapshot # Build local snapshot (no publish)
make release-dry-run # Full dry run of release processmake tidy # Run go mod tidy
make fmt # Format code
make lint # Run linters
make dev # Format, build, install (quick iteration)All scripts are in scripts/ directory:
Analyzes commits since last tag and suggests next version based on conventional commits.
Usage:
./scripts/suggest-version.shLogic:
- Breaking change commits → MAJOR bump
feat:commits → MINOR bumpfix:commits → PATCH bump
Prepares CHANGELOG.md for a new release by adding version section.
Usage:
./scripts/changelog-prepare.sh
# Prompts for version numberActions:
- Creates backup:
CHANGELOG.md.bak - Adds new
[Unreleased]section - Creates
[VERSION]section with date
Comprehensive dry run of release process.
Usage:
./scripts/release-dry-run.shChecks:
- Working directory status
- CHANGELOG.md completeness
- GoReleaser config validation
- Builds snapshot release
- Lists all artifacts
# Delete local and remote tag
git tag -d v0.1.0
git push origin :refs/tags/v0.1.0
# Recreate tag
git tag -a v0.1.0 -m "Release v0.1.0"
git push --tags# Check config
make release-check
# Test locally
make release-snapshot
# Check logs in GitHub Actions- Delete the GitHub Release (via web UI)
- Delete the tag (see above)
- Fix CHANGELOG.md
- Recreate tag and push
Option 1: Edit on GitHub
- Go to the release on GitHub
- Click "Edit release"
- Update description
Option 2: Re-release (for major issues)
- Follow "Wrong Version Released" steps
- Fix issues
- Re-release
Use Conventional Commits:
feat: add new feature
fix: resolve bug
docs: update documentation
chore: maintenance tasks
refactor: code refactoring
test: add tests
ci: CI/CD changes
Benefits:
- Auto-generated changelogs
- Semantic version suggestions
- Clear commit history
During Development:
- Add entries to
[Unreleased]section as you work - Don't wait until release time
At Release Time:
- Move unreleased items to versioned section
- Add date
- Review for completeness
Tips:
- Focus on user-facing changes
- Skip internal refactors unless significant
- Link to issues/PRs when relevant
- Use clear, non-technical language
Minimum testing checklist:
-
make testpasses -
make release-snapshotbuilds successfully - Manual smoke test of key features
- CHANGELOG.md is complete and accurate
For major releases, add:
- Test on Ubuntu 22.04 VM
- Test on Ubuntu 24.04 VM
- Test installer script
- Review all documentation
The release is automated via .github/workflows/release.yml:
name: Release
on:
push:
tags:
- 'v*'Process:
- Checkout code
- Set up Go
- Set up GoReleaser
- Run GoReleaser
- Publish to GitHub Releases
Environment Variables:
GITHUB_TOKEN: Automatically providedGITHUB_REPOSITORY_OWNER: Auto-detectedGITHUB_REPOSITORY_NAME: Auto-detected
Potential improvements to release process:
-
Automated Testing on Release
- Spin up test VMs
- Run installation tests
- Report results
-
Changelog Generation from Commits
- Use tools like
git-chglog - Auto-populate CHANGELOG.md
- Still require manual review
- Use tools like
-
Release Announcements
- Post to Discord/Slack
- Tweet from project account
- Update project website
-
Homebrew Formula
- Auto-update Homebrew tap
- Formula generation in GoReleaser
-
Debian Package
- Build
.debfiles - Maintain APT repository
- Build
If you have questions about the release process:
- Check this document
- Review
docs/RELEASE.md(technical guide) - Check existing releases for examples
- Open an issue for clarification