// "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."
| 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. |
Automated versioning and changelog generation using Changesets (monorepos) or semantic-release (single packages) with conventional commits.
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:
Both enforce Semantic Versioning (SemVer) and integrate seamlessly with CI/CD.
All version numbers follow MAJOR.MINOR.PATCH format:
Pre-release versions: 1.0.0-beta.1, 1.0.0-rc.2, 1.0.0-alpha.3
Best for:
# Install changesets
pnpm add -D @changesets/cli
# Initialize (creates .changeset directory and config)
pnpm changeset init
// .changeset/config.json
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false, // Don't auto-commit changesets
"fixed": [], // Packages that must version together
"linked": [], // Packages that share version numbers
"access": "public", // or "restricted" for private packages
"baseBranch": "main",
"updateInternalDependencies": "patch", // Bump dependent packages
"ignore": ["@repo/config", "@repo/tsconfig"] // Skip these packages
}
When making changes, developer creates a changeset file:
# Create a changeset (interactive prompts)
pnpm changeset
# Example prompts:
# ? Which packages would you like to include? ›
# ◉ @repo/ui
# ◯ @repo/utils
# ◯ @repo/config
#
# ? What kind of change is this for @repo/ui? ›
# ◯ patch - Bug fixes, internal changes
# ◉ minor - New features, backward-compatible
# ◯ major - Breaking changes
#
# ? Please enter a summary for this change:
# › Added dark mode toggle component
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
# .github/workflows/release.yml
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 Request or Publish
id: changesets
uses: changesets/action@v1
with:
# Creates a "Version Packages" PR that bumps versions
# Or publishes to npm if PR is merged
publish: pnpm release
commit: "chore: version packages"
title: "chore: version packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # If publishing to npm
- name: Send Slack notification
if: steps.changesets.outputs.published == 'true'
run: |
echo "New version published!"
# Add Slack webhook call here
What happens:
main, GitHub Action creates a "Version Packages" PRpackage.json versions and CHANGELOG.md for each package# Bump versions based on changesets
pnpm changeset version
# This updates package.json and CHANGELOG.md files
# Review changes, then commit
git add .
git commit -m "chore: version packages"
# Publish to npm
pnpm changeset publish
# Push tags to GitHub
git push --follow-tags
{
"scripts": {
"changeset": "changeset",
"changeset:version": "changeset version",
"changeset:publish": "changeset publish",
"release": "pnpm build && pnpm changeset publish"
}
}
✅ 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
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()`.
Best for:
pnpm add -D semantic-release @semantic-release/git @semantic-release/changelog
// .releaserc.js
module.exports = {
branches: ['main'],
plugins: [
// Analyze commits to determine version bump
'@semantic-release/commit-analyzer',
// Generate release notes
'@semantic-release/release-notes-generator',
// Update CHANGELOG.md
'@semantic-release/changelog',
// Update package.json version
'@semantic-release/npm',
// Commit updated files
[
'@semantic-release/git',
{
assets: ['package.json', 'CHANGELOG.md'],
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
},
],
// Create GitHub release
'@semantic-release/github',
],
}
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 changesstyle: Code style (formatting, semicolons)refactor: Code refactoringtest: Adding testschore: Maintenance tasksExamples:
# Patch release (1.0.0 → 1.0.1)
git commit -m "fix: resolve authentication timeout issue"
# Minor release (1.0.0 → 1.1.0)
git commit -m "feat: add dark mode support"
# Major release (1.0.0 → 2.0.0)
git commit -m "feat: redesign API
BREAKING CHANGE: Renamed all `get*` methods to `fetch*` for consistency"
# No release
git commit -m "docs: update README with new examples"
git commit -m "chore: upgrade dependencies"
# .github/workflows/release.yml
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write # To push tags and releases
issues: write # To comment on released issues
pull-requests: write # To comment on released PRs
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for changelog
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'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: pnpm semantic-release
✅ 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
⚠️ 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
Both approaches benefit from enforcing conventional commit format:
# Install commitlint
pnpm add -D @commitlint/cli @commitlint/config-conventional
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat', // New feature
'fix', // Bug fix
'docs', // Documentation
'style', // Formatting
'refactor', // Code refactoring
'perf', // Performance
'test', // Tests
'build', // Build system
'ci', // CI configuration
'chore', // Maintenance
'revert', // Revert commit
],
],
'scope-case': [2, 'always', 'kebab-case'],
'subject-case': [2, 'always', 'sentence-case'],
'header-max-length': [2, 'always', 72],
},
}
# lefthook.yml
commit-msg:
commands:
commitlint:
run: pnpm commitlint --edit {1}
Now every commit is validated:
git commit -m "add new feature"
# ❌ Error: subject may not be empty [subject-empty]
git commit -m "feat: add dark mode"
# ✅ Success
| 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:
# 1. Create feature branch
git checkout -b feat/dark-mode
# 2. Make changes
# ... edit code ...
# 3. Create changeset
pnpm changeset
# Select packages, choose "minor", add description
# 4. Commit everything
git add .
git commit -m "feat: add dark mode toggle"
# 5. Push and create PR
git push origin feat/dark-mode
# 6. After PR review and merge to main:
# - GitHub Action creates "Version Packages" PR automatically
# - Review version bumps and changelog
# - Merge "Version Packages" PR to publish
# 1. Create fix branch
git checkout -b fix/auth-timeout
# 2. Make changes
# ... edit code ...
# 3. Commit with conventional format
git commit -m "fix: resolve authentication timeout issue
The JWT validation was not accounting for clock skew.
Added 30-second tolerance window."
# 4. Push and create PR
git push origin fix/auth-timeout
# 5. After PR review and merge to main:
# - semantic-release automatically:
# * Bumps version (1.0.0 → 1.0.1)
# * Updates CHANGELOG.md
# * Creates GitHub release
# * Publishes to npm (if configured)
# 1. Install
pnpm add -D @changesets/cli
pnpm changeset init
# 2. Configure .changeset/config.json
# (see configuration section above)
# 3. Add GitHub Action
# (see workflow section above)
# 4. Add scripts to package.json
# "changeset": "changeset"
# "release": "pnpm build && pnpm changeset publish"
# 5. Document workflow in README
# How to create changesets, review process, etc.
# 1. Install
pnpm add -D semantic-release @semantic-release/git @semantic-release/changelog
# 2. Create .releaserc.js
# (see configuration section above)
# 3. Install commitlint
pnpm add -D @commitlint/cli @commitlint/config-conventional
# 4. Configure lefthook for commit-msg hook
# (see commitlint section above)
# 5. Add GitHub Action
# (see workflow section above)
# 6. Document commit conventions in CONTRIBUTING.md
"Versioning should be automatic, not an afterthought."
Manual changelog maintenance leads to:
Automated changelog generation ensures:
Choose the tool that fits your workflow, then trust the automation.
When agents design release processes, they should: