Go-Native Changelog Generator Tool¶
- Authors
- Matt Cockayne
- Date
- 1 April 2026
- Status
- IMPLEMENTED
Overview¶
GTB currently depends on git-cliff (Rust) to generate CHANGELOG.md at build time. This creates an external dependency that must be installed in CI and doesn't integrate with Go's tool directive. The changelog is a build artifact generated from the full git history โ it is not committed to the repository.
This spec replaces git-cliff with a pure-Go changelog generator that:
- Uses go-git to read commit history directly (no shell commands)
- Parses conventional commits and groups by version tag
- Outputs markdown compatible with
pkg/changelog.Parse() - Lives in
cmd/changelog/and is declared as a Gotooldirective - Runs via
go generateโ self-contained with zero external dependencies - Replaces git-cliff in both the GTB CLI and generated project templates
Performance¶
Benchmarked on this repository (276 commits, 49 tags): - Tag resolution: 6ms - Commit iteration and bucketing: 9ms - Total: 14ms
Extrapolated: ~40ยตs per commit. A 100,000-commit repo would complete in ~4s โ acceptable for a build-time tool.
Design¶
Command¶
cmd/changelog/
โโโ main.go โ Cobra root command
โโโ generate.go โ generate subcommand
# Generate full changelog to stdout
changelog generate
# Generate to file (creates or updates existing)
changelog generate --output CHANGELOG.md
# Generate since a specific tag
changelog generate --since v1.5.0 --output CHANGELOG.md
# Limit to N most recent releases
changelog generate --releases 10 --output CHANGELOG.md
# Include non-conventional commits
changelog generate --include-all --output CHANGELOG.md
The generate subcommand structure allows future additions (e.g. validate, diff) without breaking the CLI contract.
Tool Directive¶
In go.mod:
In go:generate:
Conventional Commit Parsing¶
The tool parses commit messages following the conventional commits spec:
Groups:
- feat โ Features
- fix โ Bug Fixes
- perf โ Performance Improvements
- refactor, docs, style, chore โ Other
- test, ci โ Skipped (not included in changelog)
- BREAKING CHANGE footer โ Breaking Changes section
Output Format¶
Matches the format pkg/changelog.Parse() expects:
# v1.2.3
### Features
* **chat:** add conversation persistence
* **output:** add spinner and progress helpers
### Bug Fixes
* **generator:** fix GitLab CI file selection
### Breaking Changes
* BREAKING CHANGE: remove deprecated GetRepo method
Implementation¶
The core logic:
- Open the repository via
git.PlainOpen(".") - Resolve all version tags (semver pattern
v*.*.*) - Iterate commits in committer-time order via
repo.Log() - Bucket each commit into its release (the tag whose commit it precedes)
- Parse each commit message for type, scope, description, and breaking changes
- Sort releases by semver (newest first)
- Format as markdown and write to output
Library Package¶
The core logic lives in pkg/changelog/generate.go (not in cmd/) so it can be reused:
// GenerateFromRepo reads git history and produces a full CHANGELOG.md string.
func GenerateFromRepo(repoPath string, opts ...GenerateOption) (string, error)
type GenerateOption func(*generateConfig)
func WithSinceTag(tag string) GenerateOption
func WithMaxReleases(n int) GenerateOption
The cmd/changelog/ is a thin CLI wrapper around this function.
Integration¶
GTB CLI¶
Replace the git-cliff copy directive with:
Generated Projects¶
The skeleton generator emits the same go:generate directive in pkg/cmd/root/generate.go. The tool directive is added to the generated go.mod.
CI Workflows¶
Remove the git-cliff action from generated GitHub Actions and GitLab CI. The changelog is generated by go generate which runs as part of the normal build.
Files¶
| File | Action |
|---|---|
cmd/changelog/ |
CREATE โ CLI tool entry point |
pkg/changelog/generate.go |
CREATE โ core generation logic |
pkg/changelog/generate_test.go |
CREATE โ tests |
go.mod |
MODIFY โ add tool directive |
internal/cmd/root/generate.go |
MODIFY โ replace copy with go tool |
internal/generator/assets/skeleton/ |
MODIFY โ remove cliff.toml, update CI templates |
internal/generator/skeleton.go |
MODIFY โ update generate.go template |
Resolved Questions¶
- Unreleased commits: Yes โ commits after the latest tag appear under an "Unreleased" heading.
- Config file: Flags and env vars only for v1. No config file.
- Merge commits: Filtered out by default. Only conventional commits are parsed by default.
--include-allflag includes non-conventional commits under "Other". - Existing file handling: If CHANGELOG.md already exists, parse it, determine which releases are already documented, and only generate missing releases. Preserves manually edited content. If the file doesn't exist, generate from scratch.