This project uses Semantic Versioning, Conventional Commits, and automated tooling to manage releases.
Releases are fully automated through a pipeline of three components:
-
PR title validation — Every PR must have a title following the Conventional Commits format (e.g.,
feat: add widget export). This is enforced by CI. -
release-please — When PRs are squash-merged to
main, release-please reads the commit messages and maintains a Release PR that tracks pending changes, updatesCHANGELOG.md, and determines the next version number. -
GoReleaser — When the Release PR is merged, release-please creates a GitHub Release with a semver tag (e.g.,
v1.2.0). GoReleaser then runs inline (in the same workflow) to build cross-platform binaries and upload them to the release.
Version numbers follow MAJOR.MINOR.PATCH:
| Change Type | Version Bump | PR Title Examples |
|---|---|---|
| Breaking change | Major (1.0.0 → 2.0.0) | feat!: redesign auth flow or any commit with a BREAKING CHANGE footer |
| New feature | Minor (1.0.0 → 1.1.0) | feat: add widget export command |
| Bug fix | Patch (1.0.0 → 1.0.1) | fix: resolve token refresh on expired credentials |
| No release | — | chore:, docs:, ci:, test:, refactor: |
Only feat and fix trigger a release. Other types (chore, docs, ci, etc.) are included in the changelog but do not bump the version on their own.
Normal flow (recommended):
- Merge PRs to
mainwith Conventional Commits titles - release-please automatically opens or updates a Release PR titled
chore(main): release X.Y.Z - Review the Release PR — verify the changelog and version bump are correct
- Merge the Release PR
- release-please creates a GitHub Release and
vX.Y.Ztag - GoReleaser builds and uploads binaries automatically
That's it. You do not need to manually create tags, write changelogs, or build binaries.
Emergency / manual release:
If you need to bypass the automated flow, manually trigger the Release Please workflow from the Actions tab, or create a tag and GitHub Release manually, then run GoReleaser locally:
goreleaser release --cleanYou will need to set the GITHUB_TOKEN environment variable and manually update CHANGELOG.md.
PR titles must follow this format:
type(optional-scope): description
Allowed types: feat, fix, docs, chore, refactor, test, perf, ci, build, revert
Breaking changes can be indicated in two ways:
- Add
!after the type:feat!: remove deprecated API - Add a
BREAKING CHANGE:footer in the PR body
GoReleaser produces binaries for:
| Platform | Architecture | Archive |
|---|---|---|
| Linux | amd64 | ei_linux_amd64.tar.gz |
| macOS | amd64 | ei_darwin_amd64.tar.gz |
| macOS | arm64 (Apple Silicon) | ei_darwin_arm64.tar.gz |
| Windows | amd64 | ei_windows_amd64.zip |
Each release also includes a checksums.txt file with SHA256 hashes.
The CLI version is set at build time via linker flags. GoReleaser runs:
go build -ldflags "-s -w -X main.version=X.Y.Z"
This replaces the default var version = "dev" in main.go. When building locally from source, the version will show as dev.
Users can update their installed binary by running:
ei updateThis checks for the latest GitHub Release and downloads the appropriate binary. The self-update mechanism uses the asset naming convention (ei_<os>_<arch>) to find the correct binary.
This project requires squash merging for all PRs to main. This ensures each PR becomes a single commit with the PR title as the commit message, which is what release-please reads to determine version bumps.
This is configured in GitHub Settings → General → Pull Requests.