Version Bookkeeping

Bulk can be used to sync version of your application to various places in code.

Basics

Bulk uses regular expressions to find versions in some file. For example, here is how we track versions in typical python project:

versions:

# There is usually a version in setup.py
- file: setup.py
  # this isn't 100% correct as version can end in different quote or
  # there might be few version parameters in a file, but this is good
  # enough for many projects, other projects might need to tweak matcher
  regex: ^\s*version\s*=\s*["']([^"']+)["']


# Also it's a good idea to put library version into
# a __version__ attribute of the module itself
- file: your_module/__init__.py
  regex: ^__version__\s*=\s*["']([^"']+)["']

Put it in bulk.yaml and now you can find out version with:

> bulk get-version
1.3.5

Yes, the first time you’ve written setup.py and __init__.py you needed to put version yourself. This is usually handled by project boilerplate.

Releasing a Project

If you obey semantic versioning in the project version run one of:

> bulk bump --breaking -g
> bulk bump --feature -g
> bulk bump --bugfix -g

The commands above will increment a major, minor or patch version of your version number, commit the changes with a comment of Version bumped to v1.3.6 and create an annotated tag v0.3.6 by starting an editor and showing you changes since previous tag. You can opt-out commit and tag creation by omitting -g which is equivalent of longer --git-commit-and-tag.

You can also use -1, -2 and -3 which increment the specific component of version. Technically they are are equivalent to above except when version is zero-based 0.x.

Note

in case of 0.x versions the version numbers are shifted. I.e. if you have two zeros numbers 0.0.x any bump with increment a single version. If you have 0.x.y number second component will increment with both --breaking and --feature. This is how many existing tools handle semver. Use -1, -2 if in doubt or to switch from 0.x versions to 1.x.

For date-based versioning use:

> bulk bump -dg

This will force your version to something like v180317.0. If you will subsequently run this command on the same day you will get v180317.1 and so forth.

Note

The date here is UTC to avoid issues with different people releasing in different timezones.

Another way to update is to use set-version:

> bulk set-version v1.3.5-beta.1
./your_module/__init__.py:1: (v1.3.5 -> v1.3.5-beta.1) __version__ = '1.3.5-beta.1'
./setup.py:6: (v1.3.5 -> v1.3.5-beta.1)       version='1.3.5-beta.1',

This is useful to set some pre-release version as you see in example because we don’t have a command-line flag for that or in case you have different version format or just want to skip version number for some reason.

Building a Pre-Release Project

Everyting above assumes that version is stored in source code and commited to git. Which is true for many tools. But you don’t want to commit version for a prerelease version of application. We have a nice command for this use case too:

> bulk with-version v1.3.6-pre4 your-build-command
1.3.5 -> 1.3.6-pre4
[ .. output of your-build-command .. ]
1.3.6-pre4 -> 1.3.5

This runs build with correct version and ensures that when build is complete you will get no version change in git status.

Since the common case is using git describe for actual version we have a shortcut for that:

> bulk with-git-version your-build-command
1.3.5 -> 1.3.5-4-gd923e59-dirty
[ .. output of your-build-command .. ]
1.3.5-4-gd923e59-dirty -> 1.3.5

(the -dirty here means you have modified git-tracked files locally)

Note

The git describe command is not strictly semver-compatible. I.e. the version x.y.z-n is treated as lower than x.y.z and you’re supposed to use x.y.z+n for that. But for now we decided to stick to what git describe provides for now. We may provide an option to fix that in future, in the meantime you can use with-version.

Other Commands

To check if version number is fine (consistent) run:

> vagga bulk check-version
setup.py:6: (v1.3.5)       version='1.3.5',
trafaret_config/__init__.py:1: (v1.3.5) __version__ = '1.3.5'

It shows you files and lines where version number is present and will fail if there is no version at all or version is inconsistent between multiple files.

Note

it will not show you files and lines which are present in config file but has no version number found. So when adding an entry in bulk.yaml you should run check-version and make sure the actual entry exists in the file.

To fix inconsistent version run:

> vagga bulk set-version v1.3.5 --force
setup.py:6: (v1.3.4 -> v1.3.5)       version='1.3.5',
trafaret_config/__init__.py:1: (v1.2.3 -> v1.3.5) __version__ = '1.3.5'

Same restriction for not found version as for check-version applies here.