These six packages address the critical workflow of managing software versions, generating changelogs, and publishing releases. auto-changelog focuses purely on generating clean changelogs from git history without enforcing commit message rules. conventional-changelog-cli is the command-line interface for the core logic that parses "Conventional Commits" to generate logs. standard-version combines version bumping, changelog generation, and git tagging into a single manual step based on commit messages. release-it is a highly configurable, interactive CLI that orchestrates the entire release process (bumping, logging, tagging, pushing, publishing) but leaves the final decision to the developer. semantic-release takes a different approach by fully automating the release pipeline in CI/CD, removing human intervention entirely based on commit history. lerna is distinct as a monorepo management tool that includes release capabilities specifically designed for handling multiple packages within a single repository, supporting both independent and fixed versioning modes.
Releasing software involves three repetitive steps: bumping the version number, generating a changelog, and publishing the package. Doing this manually leads to errors, inconsistent history, and "release fatigue." The JavaScript ecosystem offers several tools to automate this, ranging from simple changelog generators to fully autonomous CI/CD pipelines. Let's compare how auto-changelog, conventional-changelog-cli, lerna, release-it, semantic-release, and standard-version solve these problems.
The biggest divide in this ecosystem is whether you enforce a specific commit message format. Some tools parse any git history, while others require "Conventional Commits" (e.g., feat: add login).
auto-changelog works with any commit style. It groups changes by version and tries to be smart about what to show, but it doesn't enforce rules on your team.
# auto-changelog: Generates CHANGELOG.md from any git history
npx auto-changelog
conventional-changelog-cli, standard-version, release-it, and semantic-release all rely on the conventional-changelog parser. They require commits like feat:, fix:, or chore: to automatically determine version bumps and categorize log entries.
# conventional-changelog-cli: Explicitly generates log based on conventions
npx conventional-changelog-cli -p angular -i CHANGELOG.md -s
# standard-version: Bumps version AND writes changelog in one go
npx standard-version
# release-it: Uses conventional commits plugin for changelog generation
npx release-it --github.release
# semantic-release: Automatically analyzes commits in CI to generate log
npx semantic-release
lerna also uses conventional commits when configured for independent versioning, but it adds the complexity of scanning multiple packages in a monorepo to decide which ones changed.
# lerna: Versions all packages that have changes since the last release
npx lerna version --conventional-commits
How much control do you want during the release? Do you want to click a button, answer prompts, or have it happen automatically on every merge?
standard-version is a local, manual tool. You run the command, it updates your package.json, writes the changelog, and stages the files. You must still commit, tag, and push yourself.
// package.json script for standard-version
{
"scripts": {
"release": "standard-version"
}
}
// Usage: npm run release (then manually git push --follow-tags)
release-it is interactive. It shows you a summary of changes, asks if you want to bump major/minor/patch, and confirms before pushing. It feels like a co-pilot.
// .release-it.json configuration
{
"git": { "commitMessage": "Release ${version}" },
"github": { "release": true }
}
// Usage: npx release-it (follows interactive prompts)
semantic-release removes the human entirely. It runs in your CI server (GitHub Actions, GitLab CI, etc.). If you merge a feat commit to main, it immediately bumps the version, publishes to npm, and creates a GitHub Release without asking.
// .github/workflows/release.yml (simplified)
jobs:
release:
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npx semantic-release # No prompts, fully automatic
auto-changelog and conventional-changelog-cli are non-interactive utilities. They just write a file and exit. They don't bump versions or push code.
# auto-changelog: Just writes the file
npx auto-changelog --starting-version 1.0.0
lerna can be run locally or in CI. In a monorepo, it often runs in CI but might pause for confirmation depending on flags like --yes.
# lerna: Run in CI with --yes to skip prompts
npx lerna publish from-package --yes
Most tools assume you have one package.json. If you have a folder with 20 packages, things get complicated.
lerna is built specifically for this. It detects which packages changed since the last release and versions only those (independent mode) or versions everything together (fixed mode).
// lerna.json configuration for independent versioning
{
"version": "independent",
"packages": ["packages/*"]
}
// Command: Only bumps versions for packages with changes
npx lerna version --conventional-commits
semantic-release supports monorepos but requires extra plugins (like @semantic-release/git or specific monorepo plugins) to handle multiple package.json files correctly. It is not native out of the box.
// semantic-release multi-package setup requires complex plugin config
{
"plugins": [
["@semantic-release/npm", { "pkgRoot": "packages/my-lib" }],
["@semantic-release/git", { "assets": ["packages/*/package.json"] }]
]
}
release-it and standard-version generally target single packages. While you can script them to run in a loop for a monorepo, they lack the dependency graph awareness that lerna provides (e.g., knowing that if package-a changes, package-b depending on it might need an update).
# release-it: Typically run per package in a monorepo script
npx release-it --cwd packages/my-lib
auto-changelog and conventional-changelog-cli have no concept of monorepos. They simply read the git log of the current directory.
Different teams need different workflows. Some need to slack a channel, some need to update a Jira ticket, and some need to build Docker images.
release-it is famous for its plugin system. You can hook into any part of the lifecycle (before bump, after publish, etc.) to run custom scripts.
// .release-it.json with custom hooks
{
"hooks": {
"before:init": ["npm test", "npm run build"],
"after:npm:release": "echo 'Published!'"
},
"plugins": {
"@release-it/slack": { "webhook": "https://hooks.slack.com/..." }
}
}
semantic-release is also highly extensible via plugins, but the configuration is strictly defined by the plugin order (verify, analyze, generate, publish, success/fail).
// .releaserc.js for semantic-release
module.exports = {
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
["@semantic-release/npm", { "npmPublish": true }],
["@semantic-release/github", { "assets": ["dist/*"] }]
]
};
standard-version allows lifecycle scripts in package.json but is less flexible than the plugin architectures of release-it or semantic-release.
// package.json for standard-version lifecycle
{
"scripts": {
"preversion": "npm test",
"postversion": "git push && git push --tags"
}
}
lerna configuration lives in lerna.json and focuses on workspace definitions and version strategies rather than external integrations, though it can run scripts.
// lerna.json
{
"command": {
"publish": {
"ignoreChanges": ["*.md", "*.txt"]
}
}
}
auto-changelog and conventional-changelog-cli offer minimal configuration, mostly limited to template selection and output paths.
# auto-changelog: Simple config via CLI flags or .auto-changelog file
npx auto-changelog --template keepachangelog --limit 10
It is critical to note the current maintenance status of these tools before adopting them.
standard-version is officially in maintenance mode. The maintainers have stated that no new features will be added, and they recommend users migrate to release-it or semantic-release for new projects. While it still works, relying on it for long-term infrastructure carries risk.
# WARNING: standard-version is in maintenance mode
# Do not start new projects with this if you need active feature support
npx standard-version
conventional-changelog-cli is part of the larger conventional-changelog monorepo. It is stable but low-level. It is rarely used directly by end-users anymore, serving more as a building block for other tools.
auto-changelog, release-it, semantic-release, and lerna are all actively maintained and widely used in production. lerna has undergone significant architectural changes in recent versions (v6+), moving towards a more modular plugin-based approach, so ensure you are reading docs for the latest version.
| Feature | auto-changelog | conventional-changelog-cli | standard-version | release-it | semantic-release | lerna |
|---|---|---|---|---|---|---|
| Primary Goal | Generate Changelog | Parse Commits | Version + Log | Interactive Release | Auto Release (CI) | Monorepo Manager |
| Commit Style | Any | Conventional | Conventional | Any (Plugins for Conv) | Conventional | Conventional (Optional) |
| Version Bump | β No | β No | β Local | β Local/CI | β CI Only | β Local/CI |
| Publish to npm | β No | β No | β No | β Optional | β Automatic | β Automatic |
| Monorepo Ready | β No | β No | β No | β οΈ Scriptable | β οΈ Plugin Required | β Native |
| Human Approval | N/A | N/A | β Required | β Required | β None | β Optional |
| Status | Active | Active | β οΈ Maintenance | Active | Active | Active |
Choosing the right tool depends on your team's culture and project structure.
If you are running a monorepo, lerna is almost certainly your starting point for managing versions across packages. You might pair it with semantic-release for automation or release-it for manual control.
If you have a single package and want full automation (Continuous Deployment), semantic-release is the industry standard. It enforces discipline because if your tests fail or your commits are messy, the release simply doesn't happen.
If you prefer manual control but want to skip the boring typing and math, release-it is the best choice. It gives you the safety of automation without losing the ability to say "wait, let me check this first." It is also the recommended path for teams currently using standard-version.
If you just need a changelog and don't care about semantic versioning or strict commit messages, auto-changelog is the simplest, most friction-free option. It respects your existing workflow without forcing you to change how you write commits.
Final Thought: Avoid standard-version for new greenfield projects due to its maintenance status. For most modern teams, the choice is between the interactive flexibility of release-it and the strict automation of semantic-release.
Choose auto-changelog if you want a simple, zero-config changelog generator that works with any commit style. It is ideal for projects that do not want to enforce strict 'Conventional Commits' rules but still need automated release notes. Avoid this if you need automatic version bumping or npm publishing, as it only handles the changelog file.
Choose conventional-changelog-cli if you need the raw power of the conventional changelog parser as part of a custom script or build pipeline. It is best suited for teams building their own release tools on top of the standard parser rather than using an all-in-one solution. Do not use this as a standalone release manager, as it only generates the log content.
Choose lerna if you are managing a monorepo with multiple packages that need coordinated versioning and publishing. It is essential for handling dependency linking between internal packages and supports both 'fixed' (single version for all) and 'independent' versioning strategies. Avoid using it for single-package repositories, as its complexity is unnecessary for that scope.
Choose release-it if you want a flexible, interactive CLI that guides you through the release process with prompts and previews. It is perfect for developers who want automation for boring tasks (bumping, tagging, pushing) but still want to manually approve the final publish step. Select this if you need extensive plugin support for GitHub Releases, GitLab, or custom hooks without going fully automated.
Choose semantic-release if you want a fully automated, 'set and forget' release pipeline that runs in your CI/CD environment. It is the best choice for teams practicing Continuous Deployment who want to eliminate human error in versioning and ensure every merge to main potentially triggers a release. Do not use this if your release process requires manual approval gates, complex pre-release testing steps that cannot be scripted, or non-standard versioning logic.
Choose standard-version if you prefer a simple, local command to bump versions and generate changelogs based on Conventional Commits without automatic publishing. It is great for projects that want the structure of semantic versioning but prefer to handle the actual npm publish and git pushing manually. Note that this package is in maintenance mode; consider release-it or semantic-release for new projects requiring active feature development.
Command line tool for generating a changelog from git tags and commit history. Used by Modernizr, Netlify, Neutrino and Velocity.js.
npm install -g auto-changelog
Simply run auto-changelog in the root folder of a git repository. git log is run behind the scenes in order to parse the commit history.
Usage: auto-changelog [options]
Options:
-o, --output [file] # output file, default: CHANGELOG.md
-c, --config [file] # config file location, default: .auto-changelog
-t, --template [template] # specify template to use [compact, keepachangelog, json], default: compact
-r, --remote [remote] # specify git remote to use for links, default: origin
-p, --package # use version from package.json as latest release
-v, --latest-version [version] # use specified version as latest release
-u, --unreleased # include section for unreleased changes
-l, --commit-limit [count] # number of commits to display per release, default: 3
-b, --backfill-limit [count] # number of commits to backfill empty releases with, default: 3
--commit-url [url] # override url for commits, use {id} for commit id
--issue-url [url] # override url for issues, use {id} for issue id
--merge-url [url] # override url for merges, use {id} for merge id
--compare-url [url] # override url for compares, use {from} and {to} for tags
--issue-pattern [regex] # override regex pattern for issues in commit messages
--breaking-pattern [regex] # regex pattern for breaking change commits
--merge-pattern [regex] # add custom regex pattern for merge commits
--commit-pattern [regex] # pattern to include when parsing commits
--ignore-commit-pattern [regex] # pattern to ignore when parsing commits
--tag-pattern [regex] # override regex pattern for version tags
--tag-prefix [prefix] # prefix used in version tags, default: v
--autodetect-monorepo-disabled # disable monorepo autodetection, default: true (will default to false in the next major)
--starting-version [tag] # specify earliest version to include in changelog
--starting-date [yyyy-mm-dd] # specify earliest date to include in changelog
--ending-version [tag] # specify latest version to include in changelog
--sort-commits [property] # sort commits by property [relevance, date, date-desc, subject, subject-desc], default: relevance
--release-summary # display tagged commit message body as release summary
--unreleased-only # only output unreleased changes
--hide-empty-releases # hide empty releases
--hide-credit # hide auto-changelog credit
--handlebars-setup [file] # handlebars setup file
--append-git-log [string] # string to append to git log command
--append-git-tag [string] # string to append to git tag command
--prepend # prepend changelog to output file
--stdout # output changelog to stdout
--plugins [...name] # use plugins to augment commit/merge/release information
-V, --version # output the version number
-h, --help # output usage information
# Write log to CHANGELOG.md in current directory
auto-changelog
# Write log to HISTORY.md using keepachangelog template
auto-changelog --output HISTORY.md --template keepachangelog
# Disable the commit limit, rendering all commits for every release
auto-changelog --commit-limit false
auto-changelog is designed to be as flexible as possible, providing a clear changelog for any project. There are only two absolute requirements:
1.7.2 or laternpm versionThere are some less strict requirements to improve your changelog:
Install auto-changelog to dev dependencies:
npm install auto-changelog --save-dev
# or
yarn add auto-changelog --dev
Add auto-changelog -p && git add CHANGELOG.md to the version scripts in your package.json:
{
"name": "my-awesome-package",
"version": "1.0.0",
"devDependencies": {
"auto-changelog": "*"
},
"scripts": {
"version": "auto-changelog -p && git add CHANGELOG.md"
}
}
Using -p or --package uses the version from package.json as the latest release, so that all commits between the previous release and now become part of that release. Essentially anything that would normally be parsed as Unreleased will now come under the version from package.json
Now every time you run npm version, the changelog will automatically update and be part of the version commit.
Links to commits, issues, pull requests and version diffs are automatically generated based on your remote URL. GitHub, GitLab, BitBucket and Azure DevOps are all supported. If you have an unusual remote or need to override one of the link formats, use --commit-url, --issue-url or --merge-url with an {id} token. For custom version diffs, use --compare-url with {from} and {to} tokens.
# Link all issues to redmine
auto-changelog --issue-url https://www.redmine.org/issues/{id}
# Link to custom diff page
auto-changelog --compare-url https://example.com/repo/compare/{from}...{to}
If youβd like to keep an existing changelog below your generated one, just add <!-- auto-changelog-above --> to your current changelog. The generated changelog will be added above this token, and anything below will remain.
You can set any option in package.json under the auto-changelog key, using camelCase options.
{
"name": "my-awesome-package",
"version": "1.0.0",
"scripts": {
// ...
},
"auto-changelog": {
"output": "HISTORY.md",
"template": "keepachangelog",
"unreleased": true,
"commitLimit": false
}
}
You can also store config options in an .auto-changelog file in your project root:
{
"output": "HISTORY.md",
"template": "keepachangelog",
"unreleased": true,
"commitLimit": false
}
Note that any options set in package.json will take precedence over any set in .auto-changelog.
Use --tag-prefix [prefix] if you prefix your version tags with a certain string:
# When all versions are tagged like my-package/1.2.3
auto-changelog --tag-prefix my-package/
In a monorepo, each package's version tags are typically prefixed with the package name, like my-package@1.2.3. When monorepo autodetection is enabled, auto-changelog detects a monorepo package β by a repository.directory field in package.json, or an ancestor package.json declaring npm workspaces β and, for a detected package:
name in package.json (so you don't have to set --tag-prefix for every package), unless one is already configured, and## [1.2.3] rather than ## [my-package@1.2.3]), while still using the full tags for the compare links.Autodetection is controlled by --autodetect-monorepo-disabled, which defaults to true. To opt in today, set it to false:
{
"auto-changelog": {
"autodetectMonorepoDisabled": false
}
}
This option will default to false in the next major version, enabling monorepo autodetection out of the box.
By default, auto-changelog looks for valid semver tags to build a list of releases. If you are using another format (or want to include all tags), use --tag-pattern [regex]:
# When all versions are tagged like build-12345
auto-changelog --tag-pattern build-\d+
# Include any tag as a release
auto-changelog --tag-pattern .+
If you use a common pattern in your commit messages for breaking changes, use --breaking-pattern to highlight those commits as breaking changes in your changelog. Breaking change commits will always be listed as part of a release, regardless of any --commit-limit set.
auto-changelog --breaking-pattern "BREAKING CHANGE:"
By default, auto-changelog will parse GitHub-style issue fixes in your commit messages. If you use Jira or an alternative pattern in your commits to reference issues, you can pass in a custom regular expression to --issue-pattern along with --issue-url:
# Parse Jira-style issues in your commit messages, like PROJECT-418
auto-changelog --issue-pattern [A-Z]+-\d+ --issue-url https://issues.apache.org/jira/browse/{id}
Or, in your package.json:
{
"name": "my-awesome-package",
"auto-changelog": {
"issueUrl": "https://issues.apache.org/jira/browse/{id}",
"issuePattern": "[A-Z]+-\d+"
}
}
If you use a certain pattern before or after the issue number, like fixes {id}, just use a capturing group:
# "This commit fixes ISSUE-123" will now parse ISSUE-123 as an issue fix
auto-changelog --issue-pattern "[Ff]ixes ([A-Z]+-\d+)"
If you arenβt happy with the default templates or want to tweak something, you can point to a handlebars template in your local repo. Check out the existing templates to see what is possible.
Save changelog-template.hbs somewhere in your repo:
### Changelog
My custom changelog template. Donβt worry about indentation here; it is automatically removed from the output.
{{#each releases}}
Every release has a {{title}} and a {{href}} you can use to link to the commit diff.
It also has an {{isoDate}} and a {{niceDate}} you might want to use.
{{#each merges}}
- A merge has a {{message}}, an {{id}} and a {{href}} to the PR.
{{/each}}
{{#each fixes}}
- Each fix has a {{commit}} with a {{commit.subject}}, an {{id}} and a {{href}} to the fixed issue.
{{/each}}
{{#each commits}}
- Commits have a {{shorthash}}, a {{subject}} and a {{href}}, {{author}} amongst other things.
{{/each}}
{{/each}}
Then just use --template to point to your template:
auto-changelog --template changelog-template.hbs
You can also point to an external template by passing in a URL:
auto-changelog --template https://example.com/templates/compact.hbs
To see exactly what data is passed in to the templates, you can generate a JSON version of the changelog:
auto-changelog --template json --output changelog-data.json
commit-list helperUse {{#commit-list}} to render a list of commits depending on certain patterns in the commit messages:
{{#each releases}}
### [{{title}}](https://github.com/CookPete/auto-changelog/blob/HEAD/{{href}})
{{! List commits with `Breaking change: ` somewhere in the message }}
{{#commit-list commits heading='### Breaking Changes' message='Breaking change: '}}
- {{subject}} [`{{shorthash}}`](https://github.com/CookPete/auto-changelog/blob/HEAD/{{href}})
{{/commit-list}}
{{! List commits that add new features, but not those already listed above }}
{{#commit-list commits heading='### New Features' message='feat: ' exclude='Breaking change: '}}
- {{subject}} [`{{shorthash}}`](https://github.com/CookPete/auto-changelog/blob/HEAD/{{href}})
{{/commit-list}}
{{/each}}
| Option | Description |
|---|---|
heading | A heading for the list, only renders if at least one commit matches |
message | A regex pattern to match against the entire commit message |
subject | A regex pattern to match against the commit subject only |
exclude | A regex pattern to exclude from the list βΒ useful for avoiding listing commits more than once |
To insert links or other markup to PR titles and commit messages that appear in the log, use the replaceText option in your package.json:
{
"name": "my-awesome-package",
"auto-changelog": {
"replaceText": {
"(ABC-\\d+)": "[`$1`](https://issues.apache.org/jira/browse/$1)"
}
}
}
Here, any time a pattern like ABC-123 appears in your log, it will be replaced with a link to the relevant issue in Jira. Each pattern is applied using string.replace(new RegExp(key, 'g'), value).
The --handlebars-setup options allows you to point to a file to add custom Handlebars helpers, for use in custom templates using --template. Paths are relative to the directory in which you run auto-changelog.
auto-changelog --handlebars-setup setup.js --template custom-template.hbs
// setup.js
module.exports = function (Handlebars) {
Handlebars.registerHelper('custom', function (context, options) {
return 'custom helpers!'
})
}
// custom-template.hbs
Now you can use {{custom}}
See keepachangelog.com.
The command parses your git commit history and generates a changelog based on tagged versions, merged pull requests and closed issues. See a simple example in this very repo.
Because keeping a changelog can be tedious and difficult to get right. If you donβt have the patience for a hand-crafted, bespoke changelog then this makes keeping one rather easy. It also can be automated if youβre feeling extra lazy.