Add sanity check script for new version tags
Add a script to catch a few common errors in new version numbers to give the user a chance to avoid them. Set up tox and testr to support tests for the new script. Change-Id: I2a48f3de3886821611f370e04c9be13ada67edbf
This commit is contained in:
parent
654d07dad0
commit
c3ea9bb9f2
|
@ -0,0 +1,4 @@
|
|||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -s ./tests . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
|
@ -66,6 +66,11 @@ then
|
|||
title "Version $VERSION is already tagged in this repository"
|
||||
read -s -p "Press Ctrl-C to cancel or Return to continue..."
|
||||
else
|
||||
title "Sanity checking $VERSION"
|
||||
if ! $TOOLSDIR/sanity_check_version.py $VERSION $(git tag)
|
||||
then
|
||||
read -s -p "Press Ctrl-C to cancel or Return to continue..."
|
||||
fi
|
||||
TARGETSHA=`git log -1 $SHA --format='%H'`
|
||||
|
||||
title "Tagging $TARGETSHA as $VERSION"
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Check a proposed new release version against other existing versions.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
||||
def try_int(val):
|
||||
try:
|
||||
return int(val)
|
||||
except ValueError:
|
||||
return val
|
||||
|
||||
|
||||
def parse_version(v):
|
||||
parts = v.split('.')
|
||||
if len(parts) < 3:
|
||||
parts.append('0')
|
||||
return [try_int(p) for p in parts]
|
||||
|
||||
|
||||
def format_version(v):
|
||||
return '.'.join(str(p) for p in v)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'new_version',
|
||||
help='the new version being proposed',
|
||||
)
|
||||
parser.add_argument(
|
||||
'existing_versions',
|
||||
nargs='*',
|
||||
help='the existing versions in the repository',
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
new_version = parse_version(args.new_version)
|
||||
if len(new_version) < 3:
|
||||
new_version = new_version + [0]
|
||||
existing_versions = sorted([
|
||||
parse_version(v)
|
||||
for v in args.existing_versions
|
||||
])
|
||||
msgs = apply_rules(new_version, existing_versions)
|
||||
for msg in msgs:
|
||||
print(msg)
|
||||
return 1 if msgs else 0
|
||||
|
||||
|
||||
def apply_rules(new_version, existing_versions):
|
||||
if not existing_versions:
|
||||
if new_version[0] != 0:
|
||||
return ['first version in repository does not start with 0']
|
||||
return []
|
||||
if new_version in existing_versions:
|
||||
return ['version %r already exists in repository' %
|
||||
format_version(new_version)]
|
||||
same_minor = same_major = None
|
||||
for v in existing_versions:
|
||||
# Look for other numbers in the series
|
||||
if v[:2] == new_version[:2]:
|
||||
print('%r in same minor series as %r' %
|
||||
(format_version(v), format_version(new_version)))
|
||||
if not same_minor or v > same_minor:
|
||||
same_minor = v
|
||||
if v[:1] == new_version[:1]:
|
||||
print('%r in same major series as %r' %
|
||||
(format_version(v), format_version(new_version)))
|
||||
if not same_major or v > same_major:
|
||||
same_major = v
|
||||
if same_minor is not None:
|
||||
print('last version in minor series %r' %
|
||||
format_version(same_minor))
|
||||
actual = same_minor[2] + 1
|
||||
expected = new_version[2]
|
||||
if expected != actual:
|
||||
return ['new version %r increments patch version more than one over %r' %
|
||||
(format_version(new_version), format_version(same_minor))]
|
||||
if same_major is not None and same_major != same_minor:
|
||||
print('last version in major series %r' %
|
||||
format_version(same_major))
|
||||
if new_version > same_major:
|
||||
actual = same_major[1] + 1
|
||||
expected = new_version[1]
|
||||
if actual > expected:
|
||||
return ['new version %r increments minor version more than one over %r' %
|
||||
(format_version(new_version), format_version(same_major))]
|
||||
if new_version[2] != 0:
|
||||
return ['new version %r increments minor version and patch version' %
|
||||
format_version(new_version)]
|
||||
latest_version = existing_versions[-1]
|
||||
if new_version[0] > latest_version[0]:
|
||||
return ['%r is a major version increment over %r' %
|
||||
(format_version(new_version), format_version(latest_version))]
|
||||
return []
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
|
@ -9,3 +9,4 @@ oslo.sphinx
|
|||
testrepository>=0.0.17
|
||||
testscenarios>=0.4
|
||||
testtools>=0.9.32
|
||||
oslotest
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslotest import base
|
||||
import testscenarios
|
||||
|
||||
import sanity_check_version as scv
|
||||
|
||||
|
||||
load_tests = testscenarios.load_tests_apply_scenarios
|
||||
|
||||
|
||||
class ParseVersionTest(base.BaseTestCase):
|
||||
|
||||
def test_all_ints(self):
|
||||
self.assertEqual([1, 0, 1], scv.parse_version('1.0.1'))
|
||||
|
||||
def test_not_int(self):
|
||||
self.assertEqual([1, 'a', 1], scv.parse_version('1.a.1'))
|
||||
|
||||
def test_short(self):
|
||||
self.assertEqual([1, 1, 0], scv.parse_version('1.1'))
|
||||
|
||||
|
||||
class RulesTest(base.BaseTestCase):
|
||||
|
||||
scenarios = [
|
||||
|
||||
('no existing versions, 1.0.0',
|
||||
{'new_version': [1, 0, 0],
|
||||
'existing_versions': [],
|
||||
'expected': ['first version in repository does not start with 0']}),
|
||||
('no existing versions, 0.1.0',
|
||||
{'new_version': [0, 1, 0],
|
||||
'existing_versions': [],
|
||||
'expected': []}),
|
||||
|
||||
('existing series, next minor number',
|
||||
{'new_version': [1, 1, 0],
|
||||
'existing_versions': [[0, 1, 0], [1, 0, 0]],
|
||||
'expected': []}),
|
||||
('existing series, next patch number',
|
||||
{'new_version': [1, 1, 2],
|
||||
'existing_versions': [[0, 1, 0], [1, 0, 0], [1, 1, 1]],
|
||||
'expected': []}),
|
||||
('existing series, next patch number in existing minor release',
|
||||
{'new_version': [1, 1, 2],
|
||||
'existing_versions': [[0, 1, 0], [1, 0, 0], [1, 1, 1], [1, 2, 0]],
|
||||
'expected': []}),
|
||||
('existing series, extra patch number',
|
||||
{'new_version': [1, 1, 3],
|
||||
'existing_versions': [[0, 1, 0], [1, 0, 0], [1, 1, 1]],
|
||||
'expected': ["new version '1.1.3' increments patch version more than one over '1.1.1'"]}),
|
||||
('existing series, extra patch number in existing minor release',
|
||||
{'new_version': [1, 1, 3],
|
||||
'existing_versions': [[0, 1, 0], [1, 0, 0], [1, 1, 1], [1, 2, 0]],
|
||||
'expected': ["new version '1.1.3' increments patch version more than one over '1.1.1'"]}),
|
||||
|
||||
('next major number',
|
||||
{'new_version': [2, 0, 0],
|
||||
'existing_versions': [[0, 1, 0], [1, 0, 0]],
|
||||
'expected': ["'2.0.0' is a major version increment over '1.0.0'"]}),
|
||||
|
||||
# Taken from oslo.config
|
||||
('next minor, with name tags',
|
||||
{'new_version': [1, 7, 0],
|
||||
'existing_versions': [
|
||||
[1, 4, 0, '0a5'],
|
||||
[1, 5, 0],
|
||||
[1, 6, 0],
|
||||
[2013, '1b1'],
|
||||
[2013, '1b2'],
|
||||
[2013, '1b3'],
|
||||
[2013, '1b4'],
|
||||
[2013, '1b5'],
|
||||
['grizzly-eol'],
|
||||
['havana-eol'],
|
||||
],
|
||||
'expected': []}),
|
||||
]
|
||||
|
||||
def test(self):
|
||||
msgs = scv.apply_rules(self.new_version, self.existing_versions)
|
||||
self.assertEqual(self.expected, msgs)
|
5
tox.ini
5
tox.ini
|
@ -1,6 +1,6 @@
|
|||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = py26,py27,pypy,pep8
|
||||
envlist = py27,pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
|
@ -12,9 +12,10 @@ setenv =
|
|||
VIRTUAL_ENV={envdir}
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
#commands = python setup.py testr --slowest --testr-args='{posargs}'
|
||||
commands = python setup.py testr --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:pep8]
|
||||
#deps = flake8
|
||||
#commands = flake8
|
||||
|
||||
[testenv:venv]
|
||||
|
|
Loading…
Reference in New Issue