Bumpversion is automation for semantic versioning

Reading Time: 3 minutes

Semantic versioning (or just semver) is a specification for communicating about changes in code from one version to the next. It targets the problem of dependency hell.

Dependency hell is where you are when version lock and/or version promiscuity prevent you from easily and safely moving your project forward.

In short, semver specifies the first number should indicate breaking API changes, the 2nd should indicate added functionality, and the 3rd should indicate backwards-compatible bugfixes. With such a strong focus on API changes, semver is most commonly used for shared libraries. The rules of semver already help dependency management tools like Maven, NuGet, and pip understand when libraries are out of date and when they can be safely updated. Despite how many developers benefit implicitly from semver, many are unaware that semver can be easy to maintain with the right tools.

One tool that I like is bumpversion. Although it’s written in Python, it can be used in any build process to “bump versions”. On the command-line, you can use bumpversion like this:

bumpversion --current-version 0.5.1 minor src/VERSION

That would update the version number 0.5.1 to 0.6.0 in the file src/VERSION. However, if you have already wired your build to use only 1 file, then you’ve already made semver easy. Don’t add one more tool just on my say so. The more compelling usage is with a .bumpversion.cfg file in the repo. It looks something like this:

current_version = 0.5.1


This file plays the same role as the previous src/VERSION file, in that it records what the current version is. But it also stores options for bumpversion that will propagate the version to other files. In the above example, there are version numbers that need replacing in both setup.py and README.rst. I can update both with the simple command:

bumpversion minor

This time I don’t have to specify the current version, or the files that need updating. When it runs, bumpversion will modify both files, and the current_version variable. Obviously, now these 3 file changes have to be committed back to Git. Bumpversion can do that with the --commit switch or the commit = True option in the .bumpversion.cfg file. Also, it is convenient to track a release back to the code at that point. Again, bumpversion can do that, using the --tag switch or the tag = True configuration option. In either case, don’t forget to push when you’re done. I usually wait for the full build process to complete, including the upload to the appropriate artifact repository, before I push the changes. That way I can roll back the changes if things go wrong. Thanks to my colleage Nicola Paolucci’s treasure trove of git aliases, I can issue the following commands to roll-back the bump:

git reset --hard HEAD~1                        # rollback the commit
git tag -d `git describe --tags --abbrev=0`    # delete the tag

Or, you can save some time when trying out bumpversion by using the --dry-run option. That will show you the changes it’s going to make without writing them to disk. I’ve only covered the most basic options here, but there are options to account for alternate versioning schemes, or additions to standard semver, like alpha/beta builds or release candidates.

Some parts are still manual, like when you decide to run bumpversion and which kind of release bump to make. Nevertheless, I still find it a big help to make sure multiple files are updated consistently, and in accordance with the rules of semver. I hope you find it useful too. If you do or have your own tips about semantic versioning, tweet me at @devpartisan or my team at @atlassiandev.