scd was created when I worked on project which is slightly better than simple library. This project has several services (you may add “micro” prefix if you want) and a lot of plugins. This project has so many plugins that we even created cookiecutter template for that. Overall more than 10 Python packages.

These packages have dependencies and some of these dependencies were project dependencies. Such as common and some api package depend on common, you get it. And we have to pin version or put a range like >=,< or ~= (at that time pip/setuptools even do not understand ~=). Also, we had documentation and we had to manage documentation via long running stable branches. So we had to support docs for version 1.0 and 2.0. Oh, and we had DEBs/RPMs and later Docker images where we put versions in labels!

As you understand, version numbers were hardcoded everywhere. In some places this was the only way to put version number (like in this doc).

We’d been trying to use bumpversion. If worked fine for some files but become a nightmare if we wanted to make complicated replacement where regular expression will fit best. Also, it was totally impossible to use bumpversion for files where we have to put ranges like dep>={major}.{minor},<{major}.{next_minor} (no next_minor, what a pity). Yes, these files were not understand ~= at that time and please remember that not all package managers recognize such concept. It had no regular expressions and several replacements for a file. We could fork bumpversion but it was as complicated as create our own.

So here is rationale. We wanted bumpversion which:

  1. Support several search/replacement pairs for a file
  2. Support searching with regular expressions
  3. Have a named sets of replacement/search patterns because in a lot of files these could repeat a lot
  4. Have some default search/replacement pairs.
  5. Have a more reasonable configuration format than INI.
  6. Templates.
  7. Possibility to set current version and understand that numbering in files can vary even if current development version persist.
  8. Possibility to extract some information from Git to version numbers.

Let’s elaborate on those items

Reasonable Configuration File

scd has to support different configuration formats out of box. Currently it supports JSON, YAML and TOML. These formats are not ideal, but at least it is more reasonable to use them, then struggling with INI limitations.

Also, there should be autodiscovery of such files. Please check Configuration to get more details.

Several Search/Replacement Pairs

Okay, you have a package X which dependend on Y and Z. X, Y and Z all are parts of your project. Fine, and you need to bump version. Now solve problem: how to replace version range of Y and Z in setup.py of X? In a single replacement literal pattern. Yes, constant. Just because your version bumper is dumb enough to force you to simplify its life. Or with giant unsupporable regexp, yes.

We need to have a support of multiple search/replacement pairs per file. Dixi.

Named sets of Search/Replacement Pairs

If you have a lot of files where to manage version, you will quickly realise that those files are not individual, you will have ~5-6 different search and replacement patterns overall. To avoid a long list of copying and pasting, you need to have a possibility to assign pattern with some name and use it later.

For example, you can have (?<=version\s=\s")\d+\.\d+\.\d+ named as setuppy. In that case, if you will replace double quotes with single ones, you won’t sed whole file, you can do it in one place.


Why the hell on the world do you need to implement confusing serialize blocks if world already has templates? scd uses Jinja2 as templating engine.

Git and Development Releases

We live in the world where development releases exist and we need something to support them. It is great to have some base version for a current developing release but we need to have a possiblity to generate development version identifiers. Prerelases. Include build numbers.

In Python there are several projects to do that. For example, there is widely used pbr which generates development release numbers for you. There is setuptools_scm which is seriously great and I highly recommend everyone to use it.

The only problem about setuptools-scm is its extensibility. It is extendable by entrypoints and it is reasonable. But if you want to have another version numbering policy, you need to implement your own entrypoint. And put it somewhere. And set setup_requires to that package. It works, but it is slightly inconvenient to use that, having additional depenency you have to put somewhere and install before any other package. But seriously, this project rocks. And available for Python packages only so there is no way to update docs or RPM specs. And it is irritating to have 2 schemes of versioning, they will fail one day.