https://blueprints.launchpad.net/oslo/+spec/pbr-semver
OpenStack uses semver but pbr doesn’t actually understand it today. Teaching it to understand semver will allow less thought and more automation by developers, as well as better and clearer version numbers for folk doing continual deployment (CD) of OpenStack.
With postversioning (the default) given a tag of 1.3.2 pbr generates per-commit versions of 1.3.2.N.g$sha. pip currently considers such versions invalid, but if pip learns about build tags - the g$sha, then the version would be considered a release, but often the intent of an untagged commit is to be a developer version for local installs / testing. So this is not PEP-440 compliant. Conflicting with this is the desire of CD deployers and CD packagers to have monotonic versions that can be used to install packages on e.g. Debian systems, but subsequent tags can collide with these versions as well.
PEP-440 versions do not map entirely well to version_info - a core python concept but some users of pbr export version_info tuples in their module, and offering that would enable them to use pbr.
Lastly, semver provides deterministic rules for picking the next version number which at the moment is a developer task. Developers can easily get this wrong because they’re having to think ‘what version next’ vs categorising the changes in the version.
In summary:
Change from tagging versions like 1.2.3a4 (which some projects are using) to tagging them as 1.2.3.0a4 instead, and update the semver docs to specify the leading 0 accordingly. This gets us both PEP440 compatibility (which permits either 1.2.3a4 or 1.2.3.0a4 but not 1.2.3.a4) and conceptual compatibility with semver where the MAJOR.MINOR.PATCH format is strongly expected to be just numbers, which the 3a4 construct breaks. We need to continue accepting the current format tags since they already exist on our branches - but we can emit deprecation warnings.
Add pseudo-headers to commits that indicate feature / api-break / deprecation / bugfix changes. The default will be to assume bugfix. If a commit fails to mention the important changes this can be fixed up with a commit with just the metadata header and some trivial change. If a commit incorrectly claimed a significant change, we will live with the result - CD users can create packages for any commit that has landed in a branch. For instance to mark a new feature that introduces api incompatibility:
sem-ver: feature, api-break
For untagged builds, generate devN versions - e.g. x.y.z.dev4+gHASH, where x.y.z are derived by applying the semver rules with the metadata about the most recent change of each type. For pre-versioned builds, we will also generate devN builds but will not apply semver rules. The +gHASH is driven by PEP-440, and will be slightly syntax incompatible with upstream semver, but we think this is the right tradeoff. Semantically it is compatible.
Provide a new command (tag-release) to do the git tagging for developers. It will accept parameters allowing manual control, but will enforce semver is being followed (e.g. if there is a newer tag than the version being tagged in the history of the branch, that’s an error). Alpha, beta and rc tagging can be automated too.
Provide a python API for getting version_info style version data. e.g.:
import pbr.version
...
version_info = pbr.version.Version('MYPACKAGENAME').version_info()
Provide a new command (deb-version) to output Debian package version compatible version strings. This primarily involves translating PEP-440 precedence rules into Debian ~ and . component separators.
Provide a new command (rpm-version) to output RPM version metadata for incorporation in RPM versions using the ENVRA format. As RPM lacks a ‘before’ operator (~) the primary method for translation is to treat pre-release and dev builds as release builds of next lowest version to drive the sort order above all actual releases of the version below. We assume that no version will ever have more than 9998 patch/minor releases. E.g. 1.2.0.dev5 is rendered as 1.1.9999.dev5. 1.0.0.dev5 would be rendered as 0.0.9999.dev5 and finally 0.0.0.dev5 would also be rendered as 0.0.0.dev5 to avoid negative version numbers.
Provide a new command (next-version) to output the next calculated semver version. E.g. if the last release was 1.2.3 but backwards incompatibility has happened, this command would output 2.0.0. The expected use of this command is to aid developers choose the next version to tag.
sdist and the new Debian and RPM version string commands will accept a --no-rc parameter which will tell them to only ever emit versions that are a) normal versions or b) devN builds. This resolves the sorting ambiguity in semver where devN is lower than any pre-release build, but version numbers may not have both a pre-release version and a devN version component. CD deployers can then include –no-rc in their scripts and have a consistent timeline (as long as they’re pulling from a monotonically advancing branch) while regular users and packagers will want to select regular builds only.
There is one caveat here which is that new point releases of old versions must not be merged into the history of trunk, or the devN versions will be reset at that point.
We are adding a new command line option.
There are four incrementable components in a version: major, minor, patch and pre-release. The following is a description of how pbr will implement the assignment rules described by semver.rst for post-versioned numbers. For pre-versioned numbers, the user specifies the target version to use and we cannot automatically increment versions. A future spec may consider doing semver with pre-versioned version numbers in pbr, but since they are often not semver versions, and because their definition is ‘the user has chosen’, it is out of scope for now.
Using these rules a few examples may aid in clarity:
# no tags, one commit, no sem-ver:
last_tag = 0.0.0
tag_distance = 1
version = 0.0.1.dev1+gHASH
Debian version = 0.0.1~dev1+gHASH
RPM version = 0.0.0.dev1+gHASH
# tag of 0.0.1 on a commit - tag sets version.
last_tag = 0.0.1
tag_distance = 0
version = 0.0.1
Debian version = 0.0.1
RPM version = 0.0.1
# tag of 0.0.1.0a4 on a commit, 5 commits since the start
# tag sets X.Y.Z of next version, and a devN version is emitted.
no_rc = True
last_tag = 0.0.0
tag_distance = 5 # distance to origin
version = 0.0.1.dev5+gHASH
Debian version = 0.0.1~dev5+gHASH
RPM version = 0.0.0.dev5+gHASH
# tag of 0.12.2, 2 commits ago with a sem-ver: deprecation line present
# in one of them.
# However since this is a 0.x.y version, we right shift the increment.
last_tag = 0.12.2
version = 0.12.3.dev2+gHASH
Debian version = 0.12.3~dev2+gHASH
RPM version = 0.12.2.dev2+gHASH
# tag of 1.12.2, 2 commits ago with a sem-ver: deprecation line present
# in one of them.
pbr_deprecation = 1.12
version = 1.13.0.dev2+gHASH
Debian version = 0.13.0~dev2+gHASH
RPM version = 0.12.9999.dev2+gHASH
We could do nothing, but right now folk are reinventing stuff in adhoc fashions and there is no ability to reuse their solutions in a systematic fashion.
We could put deprecated etc. markers in setup.cfg (see previous iterations of this spec in gerrit). This was felt to be too tedious to maintain.
No public APIs change (since pbr has approximately no public APIs at all).
New public APIs will be added that we have to support.
None.
The needed calculations are trivial, so non-trivially slower. Reading the git history between releases is done for changelog generation already, so it will be in cache, and pulling out lines from that is trivial.
None.
Developers can largely ignore this - it will just result in PEP-440 compatible versions unless the project has chosen to start using the new features. If a project wants to opt into the new features they can do so: export version_info tuples in their projects, record when they make breaking changes and have pbr enforce appropriate version changes for them. These options are all opt-in with one exception: the change of generated numbers to be PEP-440 compatible which includes the change to issue devN for untagged versions.
This forms the beginnings of a manual for using semver in PBR.
None
ALL OF THEM.
pbr
None. It shall be perfect.
The pbr manual (in the pbr tree) needs fleshing out to cover this behaviour.
None
Note
This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode