vcver is an approach for versioning that heavily relies on the version control system of choice for determining version strings.
There are two categories of version strings:
develop versions, of the form:
{main_version}.dev{commit_count}+{branch}.{scm_change_id}
With development branches, it is desired to not override versions published from a blessed subset of "released" branches. As such, if the current branch is not a release branch, a version of 0 is used instead of the main_version:
0.dev{commit_count}+{branch}.{scm_change_id}
and a release version, of the form:
{main_version}
Each part is described as follows:
- main_version is retrieved from the last tagged commit with a leading v (e.g. v1.0), but is converted to a 0 if the branch is not a release branch.
- commit_count is the number of commits from main_version
- branch is the branch that the repository was built against, removing characters that are incompatible with PEP440 (anything that is not alphanumeric or a dot)
- scm_change_id is a unique id in the form of version control, used to identify the change that was used to build this version.
For example, in a git repository, on a master branch with the most recent tag of v1.1, the dev version would look something like:
1.1.dev10+master.abc123
On a develop branch:
0.dev13+develop.def456
And a release version:
1.1
These are compatible with PEP440.
Add a MANIFEST.in to your package, if you do not already have one. Then add the following:
include VERSION
It's also recommended to ignore this file in your version control.
Then, follow this pattern in your setup.py:
# OPTIONAL, but recommended:
# this block is useful for specifying when a build should
# use the 'release' version. it allows specifying a release version
# with an additional argument in the setup.py:
# $ python setup.py upload --release
import sys
is_release = False
if "--release" in sys.argv:
sys.argv.remove("--release")
is_release = True
# OPTIONAL, but recommended:
# vcver writes a version file relative to the
# current working directory by default.
# It's recommended to provide it with your
# setup.py directory instead (in case someone
# runs your setup.py from a different directory)
base = os.path.dirname(os.path.abspath(__file__))
setup(name='aiohttp-transmute',
# add the following two lines,
# and do not specify a version.
setup_requires=["vcver"],
# vcver will only produce a release
# version if "is_release" is True.
vcver={
"is_release": is_release,
"path": base,
# OPTIONAL ARGS
# version_format overrides the standard version format
version_format="{main_version}.{commit_count}",
# release_version_format overrides the release version format
release_version_format="{commit_count}",
# scm_type: if you do not want autodetection of the SCM, you can
# specify it.
scm_type="git",
# release_branch_regex: override the default release branch
# (default release branch depends on the SCM used.)
release_branch_regex="(master|hotfix|release)",
# version_file: override the name of the version file.
version_file="GENERATED_VERSION"
},
...
)
Now your package will publish with a VC-based version!
If you followed the full example, you can specify the release version by adding --release:
python setup.py upload --release
The dev and release versions have different goals:
- dev: to provide as much information as possible to quickly identify where the current version originated from in regard to version control.
- release: to provide a clear version that helps the consumer understand what changed.
For most consumers, the number of commits since the last release, the branch it was released against, or the build commit itself are irrelevant. The consumer wants to know about the size of the change or type of changes, and that can be done by the major / minor / patch versions specified in the git tag, or the changelog. Adding version control information proves to be confusing with that regard, providing multiple numbers that are not relevant to figuring out the amount of change.
Sometimes, a non-release version can be published accidentally, or it may be desired to publish development versions side by side by versions published by release branches. In this situations, ensuring that the release versions always take precedence over non-release version is valuable, to ensure that development versions are not accidentally picked up by those expecting stable releases.
If this behavior is not desired, custom version strings can be specified with "main_version" instead of "main_version". "main_version" is preserved regardless of the branch used.
If you followed the example, you already have this.
Once vcver is called, a VERSION file is created in the current working directory, which is typically the same directory as where the setup.py lives (you can make it more accurate, see the example)
vcver will attempt to find a VERSION file if the working directory is not a version control repository. Make sure your package includes a VERSION file by creating/modifying the MANIFEST.in:
include VERSION
Both the version format and release format is configurable, with the arguments version_format and release_version_format, respectively.
In addition to the values above, the following values are provided:
- tag_version: the raw version of main_version, which does not zero out for non-release branches.
Some (much older) versions of setuptools are unable to consume the dev version string, due to the plus in the version string.
If you need backwards compatibility and you would still like vc versioning, the following format is recommended:
{main_version}.dev{commit_count}.{branch}.{scm_change_id} This can be changed by an argument into vcver:
# in the setup call of setup.py
vcver={"version_format": "{main_version}.dev{commit_count}.{branch}.{scm_change_id}"}
Semantic versioning is a standard to provided a meaning to the major, minor, and patch versions of a version string. Compatibility with semver is possible if new major / minor versions are tagged according the semver spec.
- Zillow, where this particular approach of SCM-based versioning was developed
- Taylor McKay, who implemented the original Python version at Zillow
- Mohammad Sarhan, who designed and implemented the original Java version at Zillow, and has a public gradle variant