diff options
author | Jenkins <jenkins@review.openstack.org> | 2014-09-03 14:11:58 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2014-09-03 14:11:58 +0000 |
commit | 3603c5453d6e29ca48cfcd16816e8f69f4845bfd (patch) | |
tree | abbbeacbf04bdc8d2d63d8f52e26ac4ffbf3684b | |
parent | 886a95f8d00e8d3eb344874fbb63905aad985e41 (diff) | |
parent | c1c99a7c317c5dc4bdac9a9f8f10e96e5a658b8f (diff) | |
download | pbr-3603c5453d6e29ca48cfcd16816e8f69f4845bfd.tar.gz |
Merge "Look for and process sem-ver pseudo headers in git"
-rw-r--r-- | doc/source/index.rst | 10 | ||||
-rw-r--r-- | pbr/packaging.py | 39 | ||||
-rw-r--r-- | pbr/tests/test_packaging.py | 59 |
3 files changed, 104 insertions, 4 deletions
diff --git a/doc/source/index.rst b/doc/source/index.rst index 897f3e5..fa77de1 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -36,6 +36,16 @@ If a given revision is tagged, that's the version. If it's not, then we take the last tagged version number and increment it to get a minimum target version. +We then walk git history back to the last release. Within each commit we look +for a sem-ver: pseudo header, and if found parse it looking for keywords. +Unknown symbols are not an error (so that folk can't wedge pbr or break their +tree), but we will emit an info level warning message. Known symbols: +``feature``, ``api-break``, ``deprecation``, ``bugfix``. A missing +sem-ver line is equivalent to ``sem-ver: bugfix``. The ``bugfix`` symbol causes +a patch level increment to the version. The ``feature`` and ``deprecation`` +symbols cause a minor version increment. The ``api-break`` symbol causes a +major version increment. + If postversioning is in use, we use the resulting version number as the target version. diff --git a/pbr/packaging.py b/pbr/packaging.py index 3f35593..e2fec70 100644 --- a/pbr/packaging.py +++ b/pbr/packaging.py @@ -776,6 +776,43 @@ def have_sphinx(): return _have_sphinx +def _get_increment_kwargs(git_dir, tag): + """Calculate the sort of semver increment needed from git history. + + Every commit from HEAD to tag is consider for sem-ver metadata lines. + See the pbr docs for their syntax. + + :return: a dict of kwargs for passing into SemanticVersion.increment. + """ + result = {} + if tag: + version_spec = tag + "..HEAD" + else: + version_spec = "HEAD" + changelog = _run_git_command(['log', version_spec], git_dir) + header_len = len(' sem-ver:') + commands = [line[header_len:].strip() for line in changelog.split('\n') + if line.startswith(' sem-ver:')] + symbols = set() + for command in commands: + symbols.update([symbol.strip() for symbol in command.split(',')]) + + def _handle_symbol(symbol, symbols, impact): + if symbol in symbols: + result[impact] = True + symbols.discard(symbol) + _handle_symbol('bugfix', symbols, 'patch') + _handle_symbol('feature', symbols, 'minor') + _handle_symbol('deprecation', symbols, 'minor') + _handle_symbol('api-break', symbols, 'major') + for symbol in symbols: + log.info('[pbr] Unknown sem-ver symbol %r' % symbol) + # We don't want patch in the kwargs since it is not a keyword argument - + # its the default minimum increment. + result.pop('patch', None) + return result + + def _get_revno_and_last_tag(git_dir): """Return the commit data about the most recent tag. @@ -813,7 +850,7 @@ def _get_version_from_git_target(git_dir, target_version): ['log', '-n1', '--pretty=format:%h'], git_dir) tag, distance = _get_revno_and_last_tag(git_dir) last_semver = version.SemanticVersion.from_pip_string(tag or '0') - new_version = last_semver.increment() + new_version = last_semver.increment(**_get_increment_kwargs(git_dir, tag)) if target_version is not None and new_version > target_version: raise ValueError( "git history requires a target version of %(new)s, but target " diff --git a/pbr/tests/test_packaging.py b/pbr/tests/test_packaging.py index 1fe8659..71ce6d3 100644 --- a/pbr/tests/test_packaging.py +++ b/pbr/tests/test_packaging.py @@ -73,12 +73,15 @@ class TestRepo(fixtures.Fixture): 'example@example.com'], self._basedir) base._run_cmd(['git', 'add', '.'], self._basedir) - def commit(self): + def commit(self, message_content='test commit'): files = len(os.listdir(self._basedir)) path = self._basedir + '/%d' % files open(path, 'wt').close() base._run_cmd(['git', 'add', path], self._basedir) - base._run_cmd(['git', 'commit', '-m', 'test commit'], self._basedir) + base._run_cmd(['git', 'commit', '-m', message_content], self._basedir) + + def uncommit(self): + base._run_cmd(['git', 'reset', '--hard', 'HEAD^'], self._basedir) def tag(self, version): base._run_cmd( @@ -237,6 +240,20 @@ class TestVersions(base.BaseTestCase): version = packaging._get_version_from_git() self.assertThat(version, matchers.StartsWith('1.2.4.dev1.g')) + def test_untagged_version_minor_bump(self): + self.repo.commit() + self.repo.tag('1.2.3') + self.repo.commit('sem-ver: deprecation') + version = packaging._get_version_from_git() + self.assertThat(version, matchers.StartsWith('1.3.0.dev1.g')) + + def test_untagged_version_major_bump(self): + self.repo.commit() + self.repo.tag('1.2.3') + self.repo.commit('sem-ver: api-break') + version = packaging._get_version_from_git() + self.assertThat(version, matchers.StartsWith('2.0.0.dev1.g')) + def test_untagged_version_has_dev_version_preversion(self): self.repo.commit() self.repo.tag('1.2.3') @@ -244,7 +261,7 @@ class TestVersions(base.BaseTestCase): version = packaging._get_version_from_git('1.2.5') self.assertThat(version, matchers.StartsWith('1.2.5.dev1.g')) - def test_preversion_too_low(self): + def test_preversion_too_low_simple(self): # That is, the target version is either already released or not high # enough for the semver requirements given api breaks etc. self.repo.commit() @@ -256,6 +273,42 @@ class TestVersions(base.BaseTestCase): ValueError, packaging._get_version_from_git, '1.2.3') self.assertThat(err.args[0], matchers.StartsWith('git history')) + def test_preversion_too_low_semver_headers(self): + # That is, the target version is either already released or not high + # enough for the semver requirements given api breaks etc. + self.repo.commit() + self.repo.tag('1.2.3') + self.repo.commit('sem-ver: feature') + # Note that we can't target 1.2.4, the feature header means we need + # to be working on 1.3.0 or above. + err = self.assertRaises( + ValueError, packaging._get_version_from_git, '1.2.4') + self.assertThat(err.args[0], matchers.StartsWith('git history')) + + def test_get_kwargs_corner_cases(self): + # No tags: + git_dir = self.repo._basedir + '/.git' + get_kwargs = lambda tag: packaging._get_increment_kwargs(git_dir, tag) + + def _check_combinations(tag): + self.repo.commit() + self.assertEqual(dict(), get_kwargs(tag)) + self.repo.commit('sem-ver: bugfix') + self.assertEqual(dict(), get_kwargs(tag)) + self.repo.commit('sem-ver: feature') + self.assertEqual(dict(minor=True), get_kwargs(tag)) + self.repo.uncommit() + self.repo.commit('sem-ver: deprecation') + self.assertEqual(dict(minor=True), get_kwargs(tag)) + self.repo.uncommit() + self.repo.commit('sem-ver: api-break') + self.assertEqual(dict(major=True), get_kwargs(tag)) + self.repo.commit('sem-ver: deprecation') + self.assertEqual(dict(major=True, minor=True), get_kwargs(tag)) + _check_combinations('') + self.repo.tag('1.2.3') + _check_combinations('1.2.3') + def load_tests(loader, in_tests, pattern): return testscenarios.load_tests_apply_scenarios(loader, in_tests, pattern) |