commit-and-tag-version: the standard-version fork that kept the old habit alive
Current fork of the excellent standard-version. Automate versioning and CHANGELOG generation, with semver.org and conventionalcommits.org :trophy:
At a glance
- What is it?
- commit-and-tag-version is a JavaScript fork of the deprecated standard-version tool that reads Conventional Commits, bumps version files across languages, writes a CHANGELOG and creates the git tag and release commit, and it exists because its maintainers could not move to release-please. It suits a team already on the standard-version conventions, and it is a maintenance decision as much as a tool.
- Who is it for?
- commit-and-tag-version is the right choice for a team whose release process already runs standard-version, because swapping the binary in place keeps the same behaviour, the same config key and the same changelog format, and because the maintainers are explicit that the tool exists for people who cannot move to release-please. It is the wrong choice for a project starting fresh, since the upstream's own recommendation is a different tool entirely.
- Can I use it commercially?
- Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The fork exists because the original was deprecated
The first thing the project documentation says is that commit-and-tag-version is a fork of standard-version, and the reason is stated in the same paragraph: because of maintainer availability, standard-version was deprecated on 15th May 2022, and the previous maintainer recommends release-please as an alternative for those using GitHub Actions. The fork exists for people who cannot switch to release-please, or who would like to continue using standard-version.
So the project is, by its own description, a continuity project rather than a new idea. The description in the repository metadata says it is the current fork of the excellent standard-version, automating versioning and CHANGELOG generation with semver.org and conventionalcommits.org.
The upstream was a well-liked tool, and the fork inherits its conventions wholesale. That inheritance is the point for an adopting team, because the migration question is the question almost everyone asks. The documentation answers it directly: to migrate, you can drop in commit-and-tag-version in place of standard-version. There are no changes in 9.5.0 other than to add the package.json config key commit-and-tag-version, and the previous configuration key standard-version will still work.
The naming has a stated rationale too. The author did not want to scope the package or name it standard-version-fork, wanted the purpose of the tool to be clearer, and wanted to distinguish it from the other tool in the same organisation, absolute-version, which just prints version information for pre-releases.
Five steps from Conventional Commits to a tag and a release commit
The mechanism is documented as a two step user process and a five step tool process, and it is worth reading because each step maps to a file or a piece of state you can inspect.
The user follows the Conventional Commits Specification in the repository, and when it is time to release, runs commit-and-tag-version.
The tool then retrieves the current version of the repository by looking at packageFiles, falling back to the last git tag. It bumps the version in bumpFiles based on the commits. It generates a changelog based on the commits, using conventional-changelog under the hood. It creates a new commit including the bumped files and the updated CHANGELOG. And it creates a new tag with the new version number.
So the release is a commit and a tag, and the changelog is generated from commit messages rather than written by hand. The version comes from a configured file when there is one, and from the tag history when there is not, which is what makes a fresh repository work on the first run without any version to read.
The package description calls it a replacement for npm version with automatic CHANGELOG generation, and the semantics of the commit messages are what drive everything. The Conventional Commits badge in the readme and the specification link in the steps make the dependency on the commit format explicit, which is the one thing a team has to get right before anything else works.
packageFiles, bumpFiles and updaters, and why the distinction exists
Three concepts handle the files, and the distinction between them is the part of the configuration most teams only meet when they have a file the defaults do not cover.
packageFiles are user-defined files where versions can be read from and bumped. The examples given are package.json and manifest.json, and in most cases, including the default, packageFiles are a subset of bumpFiles.
bumpFiles are user-defined files where versions should be bumped, but not explicitly read from. The examples are package-lock.json and npm-shrinkwrap.json. That is the distinction: a lockfile is written with the new version but the tool does not take the current version from it.
updaters are the simple modules used for reading packageFiles and writing to bumpFiles. They are the extension point, and the documentation has a section on custom updaters with two required functions, readVersion taking contents and returning a string, and writeVersion taking contents and a version and returning a string.
The default assumption is that you are working in a NodeJS based project, and the documentation says that for the majority of projects you might never need to interact with these options. That is true for JavaScript projects, and the reason the concepts exist at all is that the tool is also used for Maven, Gradle, .NET, YAML, OpenAPI and Python, which is where a package.json-shaped assumption breaks.
Other languages, one flag at a time
The language support section is short because the mechanism is the same in each case. For Maven, you point at your pom.xml file:
commit-and-tag-version --packageFiles pom.xml --bumpFiles pom.xmlThe same shape applies elsewhere, and the sections exist for Gradle, .NET, YAML, OpenAPI and Python. The two flags are the whole interface: packageFiles says where to read the current version from, bumpFiles says where to write the new one.
That design is worth pausing on, because it is what makes a generic tool usable across ecosystems without writing a plugin. The tool already knows how to parse a package.json and a lockfile; pointing it at a pom.xml means it needs to know how to find and replace a version element in XML, and that knowledge lives in an updater module rather than in the command line.
The flip side is that the tool is opinionated about the shape of the file it is given. A pom.xml with an unusual version layout, a .csproj with several version fields, or a YAML file where the version appears in several places are the cases where a custom updater is the answer, and the documentation says exactly that.
Installing it, and the Node version floor
The installation section is organised by how you intend to run it, and the first two are the ones most teams use.
As a local npm run script, the tool is added to the project and invoked through the package scripts, and the release script in the package.json is wired to the binary at bin/cli.js. As a global bin, the tool is installed globally and the command is available on the path. And using npx, the tool is run without a permanent install.
The runtime requirement is the notable part. The engines field in package.json asks for Node 22 or later, and the release history explains why the floor moved. Version 10.x drops support for deprecated node versions, 11.x is a formatting change if you are relying on the exact markdown format in the changelog, and 12.x drops support for node 14 and 16.
So there is a three-part upgrade story for an existing standard-version user, and only one part is invisible. The Node floor moves twice and lands on Node 22, which is a long way up from the Node 14 and 16 that standard-version-era projects ran on. A team still on Node 16 in a build image is not going to be able to take the current release, and the 11.x changelog formatting change is the one that will show up as a diff in your CHANGELOG.md rather than as a build failure.
The rest of the repository supports the picture of a maintained, actively developed project. There is a .versionrc file for configuration, defaults.js and command.js at the root, lib/ for the implementation, a test/ directory, a vitest.config.js, an eslint.config.mjs, a .prettierrc, and both LICENSE.txt and MIGRATION.md. The test scripts are split into unit and git integration runs, with the integration test named test/git.integration-test.js, and the release script is the CLI binary itself, which is how a version bump is made in this repository.
Where commit-and-tag-version is the wrong tool
The first limit is that the project tells you to use something else. The upstream maintainer recommends release-please, and the fork's own documentation names it as the alternative for anyone using GitHub Actions. Adopting a fork whose documentation points at a different tool is a defensible position when you cannot change the surrounding pipeline, and a strange one otherwise.
The second is the release-please difference in approach, which is worth understanding if you are choosing. The documentation names release-please as the alternative for projects that use GitHub Actions. commit-and-tag-version is a command you run locally or in a job, and it operates on one project directory at a time. For a monorepo with several independently released packages, that is a structural difference rather than a detail, which is where the choice matters.
The third is the dependency on commit message discipline. Conventional Commits is a convention, and a repository that has not followed it will produce a changelog that is a list of whatever the commits happened to say. The tool does not validate that your history follows the specification, so the failure mode is a bad changelog rather than an error, and the fix is a contributor process rather than a configuration.
The fourth is the Node floor again, from a different angle. A tool that requires Node 22 in a repository whose CI runs on Node 18 is a build matrix change, and for a team that upgraded for other reasons that is a reason to look at the alternative once more.
The fifth is the fork's own lifetime. This is a project maintained by the people who needed it maintained, and the release cadence in the tags is fast, v13.2.1, v13.2.2 and v13.3.0 within about a month. That is good for a maintenance fork, and it is also a signal about who the users are: they are the ones who could not move, which is a shrinking group over time.
Editorial conclusion
commit-and-tag-version is the right choice for a team whose release process already runs standard-version, because swapping the binary in place keeps the same behaviour, the same config key and the same changelog format, and because the maintainers are explicit that the tool exists for people who cannot move to release-please. It is the wrong choice for a project starting fresh, since the upstream's own recommendation is a different tool entirely. Before you adopt it, check the three things the project states: that the config key changes from standard-version to commit-and-tag-version even though the old key keeps working, that version 10 drops deprecated Node versions, 11 changes the changelog markdown format and 12 drops Node 14 and 16, and that the current release requires Node 22 or later, which is a floor that reaches back further than most Node tooling in a repository today.
Frequently asked questions
How do I migrate from standard-version to commit-and-tag-version?
The project documentation says you can drop in commit-and-tag-version in place of standard-version. There are no changes in 9.5.0 other than adding the package.json config key commit-and-tag-version, and the previous standard-version key will still work. Later versions moved the runtime floor: 10.x drops deprecated Node versions, 11.x changes the changelog markdown format, and 12.x drops Node 14 and 16.
What is the difference between packageFiles and bumpFiles in commit-and-tag-version?
packageFiles are files where the version can be read from and bumped, such as package.json or manifest.json, while bumpFiles are files where the version is written but not read from, such as package-lock.json. Updaters are the modules that do the reading and writing, and you can write your own with readVersion and writeVersion for files the defaults do not cover.
Does commit-and-tag-version work with Maven and Gradle projects?
Yes. For Maven you point the tool at your pom.xml with commit-and-tag-version --packageFiles pom.xml --bumpFiles pom.xml, and the same packageFiles and bumpFiles flags are used for the Gradle, .NET, YAML, OpenAPI and Python cases the documentation describes.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/absolute-version-commit-and-tag-version)Was this useful?