| name | changelog-automation |
| description | Apply changelog automation and semantic versioning patterns using Changesets or semantic-release: conventional commits, automated version bumping, release notes generation. Use when setting up release workflows, discussing versioning, or implementing changelog automation. |
Changelog Automation
Automated versioning and changelog generation using Changesets (monorepos) or semantic-release (single packages) with conventional commits.
Philosophy
Manual changelog maintenance is error-prone and time-consuming. Automate version bumping, changelog updates, and release notes generation based on commit history or explicit change declarations.
Two proven approaches:
- Changesets: PR-based workflow where developers declare intent in separate files (best for monorepos)
- Semantic-release: Commit-based workflow using conventional commits (best for single packages)
Both enforce Semantic Versioning (SemVer) and integrate seamlessly with CI/CD.
Semantic Versioning (SemVer)
All version numbers follow MAJOR.MINOR.PATCH format:
- MAJOR (1.0.0 → 2.0.0): Breaking changes, incompatible API changes
- MINOR (1.0.0 → 1.1.0): New features, backward-compatible additions
- PATCH (1.0.0 → 1.0.1): Bug fixes, backward-compatible fixes
Pre-release versions: 1.0.0-beta.1, 1.0.0-rc.2, 1.0.0-alpha.3
Changesets (Recommended for Monorepos)
Best for:
- Monorepos with multiple packages
- Teams that want explicit change declarations
- Projects where commit messages may not be reliable
- Gradual migration (works alongside existing processes)
Installation
pnpm add -D @changesets/cli
pnpm changeset init
Configuration
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@repo/config", "@repo/tsconfig"]
}
Workflow
1. Developer Creates Changeset
When making changes, developer creates a changeset file:
pnpm changeset
This creates .changeset/random-name.md:
---
"@repo/ui": minor
---
Added dark mode toggle component with theme persistence
Commit the changeset file along with your code changes:
git add .changeset/random-name.md src/
git commit -m "feat: add dark mode toggle"
git push
2. Automated Release (GitHub Actions)
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Create Release Pull
What happens:
- When changesets are merged to
main, GitHub Action creates a "Version Packages" PR
- This PR updates
package.json versions and CHANGELOG.md for each package
- When you merge the "Version Packages" PR, packages are published automatically
- Git tags are created for each release
3. Manual Release
pnpm changeset version
git add .
git commit -m "chore: version packages"
pnpm changeset publish
git push --follow-tags
Package.json Scripts
{
"scripts": {
"changeset": "changeset",
"changeset:version": "changeset version",
"changeset:publish": "changeset publish",
"release": "pnpm build && pnpm changeset publish"
}
}
Advantages
✅ Explicit intent: Developers declare what changed and why
✅ Flexible timing: Create changeset anytime, release when ready
✅ Multi-package: Handles complex monorepo dependencies automatically
✅ PR-based: Fits naturally into PR workflow
✅ Gradual adoption: Can coexist with other versioning strategies
✅ Snapshot releases: Easy to create pre-release versions for testing
Example Changeset Files
Bug fix (patch):
---
"@repo/api": patch
---
Fixed race condition in authentication middleware
New feature (minor):
---
"@repo/ui": minor
"@repo/utils": patch
---
Added new `DataTable` component with sorting and filtering.
Updated `formatDate` utility to handle more formats.
Breaking change (major):
---
"@repo/api": major
---
BREAKING: Renamed `getUser()` to `fetchUser()` for consistency.
Migration: Replace all `getUser()` calls with `fetchUser()`.
Semantic-release (Recommended for Single Packages)
Best for:
- Single-package repositories
- Teams committed to conventional commits
- Fully automated release process
- Strict versioning discipline
Installation
pnpm add -D semantic-release @semantic-release/git @semantic-release/changelog
Configuration
module.exports = {
branches: ['main'],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/changelog',
'@semantic-release/npm',
[
'@semantic-release/git',
{
assets: ['package.json', 'CHANGELOG.md'],
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
},
],
'@semantic-release/github',
],
}
Conventional Commits
Semantic-release requires conventional commit format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types that trigger releases:
feat: New feature (bumps MINOR)
fix: Bug fix (bumps PATCH)
perf: Performance improvement (bumps PATCH)
BREAKING CHANGE: in footer (bumps MAJOR)
Types that don't trigger releases:
docs: Documentation changes
style: Code style (formatting, semicolons)
refactor: Code refactoring
test: Adding tests
chore: Maintenance tasks
Examples:
git commit -m "fix: resolve authentication timeout issue"
git commit -m "feat: add dark mode support"
git commit -m "feat: redesign API
BREAKING CHANGE: Renamed all `get*` methods to `fetch*` for consistency"
git commit -m "docs: update README with new examples"
git commit -m "chore: upgrade dependencies"
GitHub Actions Workflow
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@v2
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
Advantages
✅ Fully automated: No manual version bumping or changelog updates
✅ Commit-based: Version determined from commit messages
✅ Consistent: Enforces conventional commits
✅ Immediate: Release happens automatically on merge to main
✅ Rich integrations: GitHub releases, npm publishing, Slack notifications
Disadvantages
⚠️ Strict commits required: Team must follow conventional commits religiously
⚠️ Less control: Version bump determined by commits, not explicit choice
⚠️ Single package focus: Monorepos need additional configuration
⚠️ Can't defer releases: Every merge to main triggers analysis
Enforcing Conventional Commits
Both approaches benefit from enforcing conventional commit format:
Commitlint Setup
pnpm add -D @commitlint/cli @commitlint/config-conventional
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'build',
'ci',
'chore',
'revert',
],
],
'scope-case': [2, 'always', 'kebab-case'],
'subject-case': [2, 'always', 'sentence-case'],
'header-max-length': [2, 'always', 72],
},
}
Lefthook Integration
commit-msg:
commands:
commitlint:
run: pnpm commitlint --edit {1}
Now every commit is validated:
git commit -m "add new feature"
git commit -m "feat: add dark mode"
Comparison: Changesets vs Semantic-release
| Feature | Changesets | Semantic-release |
|---|
| Best for | Monorepos | Single packages |
| Workflow | PR-based (explicit files) | Commit-based (conventional commits) |
| Automation | Semi-automated | Fully automated |
| Control | High (manual declaration) | Low (commit-driven) |
| Flexibility | Create changeset anytime | Must follow commit conventions |
| Learning curve | Low | Medium (conventional commits) |
| Monorepo support | Excellent | Requires plugins |
| Release timing | When "Version Packages" PR merged | Immediately on merge to main |
| Team discipline | Low (just create changeset files) | High (strict commit format) |
Recommendation:
- Monorepo with 2+ packages: Use Changesets
- Single package, disciplined team: Use semantic-release
- Single package, less strict team: Use Changesets
- Need gradual adoption: Use Changesets
Example Workflows
Changesets: New Feature
git checkout -b feat/dark-mode
pnpm changeset
git add .
git commit -m "feat: add dark mode toggle"
git push origin feat/dark-mode
Semantic-release: Bug Fix
git checkout -b fix/auth-timeout
git commit -m "fix: resolve authentication timeout issue
The JWT validation was not accounting for clock skew.
Added 30-second tolerance window."
git push origin fix/auth-timeout
Best Practices
Do ✅
- Choose one approach: Either Changesets OR semantic-release, not both
- Enforce conventional commits: Use commitlint for consistency
- Automate with CI/CD: GitHub Actions for releases
- Write clear changeset descriptions: Future you will thank you
- Review version bumps: Check generated changelog before releasing
- Use semantic versioning: Follow MAJOR.MINOR.PATCH strictly
- Tag releases: Create git tags for every release
- Generate release notes: Automate from changeset files or commits
Don't ❌
- Don't manually edit CHANGELOG.md: Let automation handle it
- Don't skip changesets: Every PR should have a changeset (if using Changesets)
- Don't ignore breaking changes: Always document in changeset/commit
- Don't publish without CI: Automate to prevent human error
- Don't version together unnecessarily: Independent versioning where possible
- Don't forget to build: Run build before publish
Quick Setup Guide
For Changesets (Monorepo)
pnpm add -D @changesets/cli
pnpm changeset init
For semantic-release (Single Package)
pnpm add -D semantic-release @semantic-release/git @semantic-release/changelog
pnpm add -D @commitlint/cli @commitlint/config-conventional
Philosophy
"Versioning should be automatic, not an afterthought."
Manual changelog maintenance leads to:
- Forgotten changes
- Incorrect version bumps
- Inconsistent release notes
- Wasted developer time
Automated changelog generation ensures:
- Every change is documented
- Versions follow semantic versioning
- Release notes are comprehensive
- Developers focus on code, not process
Choose the tool that fits your workflow, then trust the automation.
When agents design release processes, they should:
- Recommend Changesets for monorepos with multiple packages
- Recommend semantic-release for single-package repositories
- Enforce conventional commits with commitlint + Lefthook
- Automate versioning and changelog with GitHub Actions
- Follow semantic versioning (MAJOR.MINOR.PATCH) strictly
- Generate release notes from changesets or commits
- Create git tags for every release
- Document workflow clearly for team