add stable-branch-type option

The rules for stable branches vary based on whether a project is tagged
or not. The default rule set still relies on using tags, but projects
like devstack and grenade will want to be able to create branches
without having to tag. This patch adds a new stable-branch-type option
to let us control the behavior for each deliverable file.

Change-Id: I13c22d9da303cd3a6df329bf310a2292cfec9ae0
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann 2017-05-25 13:27:59 -04:00
parent a0c94e59f8
commit d8c990841c
3 changed files with 189 additions and 17 deletions

View File

@ -355,6 +355,26 @@ The top level of a deliverable file is a mapping with keys:
``releases``
A list of the releases for the deliverable.
``stable-branch-type``
This (optional) key sets the validation for the location associated
with each stable branch.
``std``
Default: Requires stable branches to be created from tagged
releases. This is the correct branch type for most projects.
The location must be either an existing version tag or the most
recently added version number under the releases list (allowing a
tag and branch to be submitted together). All repositories
associated with the version (as identified by the deliverable
file) will be branched from that version using the name given.
``tagless``
This mode requires stable branch locations to be a mapping between
repository name and an existing commit, specified by the
hash. This mode should only be used for projects that do not tag
releases, such as devstack and grenade.
``branches``
A list of the branches for the deliverable.
@ -393,12 +413,8 @@ Each ``branch`` entry is a mapping with keys:
``location``
The location value depends on the name.
If a branch name starts with stable/ then the location must be
either an existing version tag or the most recently added version
number under the releases list (allowing a tag and branch to be
submitted together). All repositories associated with the version
(as identified by the deliverable file) will be branched from that
version using the name given.
If a branch name starts with stable/ then the location value depends
on the ``stable-branch-type`` setting.
If a branch name starts with feature/ then the location must be a
mapping between the target repository name and the SHA of a commit

View File

@ -553,6 +553,9 @@ def validate_stable_branches(deliverable_info, series_name,
if ('launchpad' in deliverable_info and
deliverable_info['launchpad'] in _NO_STABLE_BRANCH_CHECK):
return
branch_mode = deliverable_info.get('stable-branch-type', 'std')
branches = deliverable_info.get('branches', [])
known_releases = list(
r['version']
@ -566,18 +569,49 @@ def validate_stable_branches(deliverable_info, series_name,
prefix, series = branch['name'].split('/')
if prefix != 'stable':
continue
if not isinstance(branch['location'], six.string_types):
location = branch.get('location')
if branch_mode == 'std':
if not isinstance(location, six.string_types):
mk_error(
('branch location for %s is '
'expected to be a string but got a %s' % (
branch['name'], type(location)))
)
if location not in known_releases:
mk_error(
('stable branches must be created from existing '
'tagged releases, and %s for %s is not found in the '
'list of releases for this deliverable' % (
location, branch['name']))
)
elif branch_mode == 'tagless':
if not isinstance(location, dict):
mk_error(
('branch location for %s is '
'expected to be a mapping but got a %s' % (
branch['name'], type(location)))
)
# The other rules aren't going to be testable, so skip them.
continue
for repo, loc in sorted(location.items()):
if not is_a_hash(loc):
mk_error(
('tagless stable branches should be created '
'from commits by SHA but location %s for '
'branch %s of %s does not look '
'like a SHA' % (
(loc, repo, branch['name'])))
)
if not gitutils.commit_exists(repo, loc):
mk_error(
('stable branches should be created from merged '
'commits but location %s for branch %s of %s '
'does not exist' % (
(loc, repo, branch['name'])))
)
else:
mk_error(
('branch location for %s is '
'expected to be a string but got a %s' % (
branch['name'], type(branch['location'])))
)
if branch['location'] not in known_releases:
mk_error(
('stable branches must be created from existing '
'tagged releases, and %s for %s is not found in the '
'list of releases for this deliverable' % (
branch['location'], branch['name']))
('unrecognized stable-branch-type %r' % (branch_mode,))
)
if series_name == '_independent':
if series not in known_series:

View File

@ -1234,6 +1234,128 @@ class TestValidateStableBranches(base.BaseTestCase):
self.assertEqual(0, len(warnings))
self.assertEqual(0, len(errors))
def test_explicit_stable_branch_type(self):
deliverable_data = textwrap.dedent('''
stable-branch-type: std
releases:
- version: 1.5.0
projects:
- repo: openstack/automaton
hash: be2885f544637e6ee6139df7dc7bf937925804dd
branches:
- name: stable/ocata
location: 1.5.0
''')
warnings = []
errors = []
deliverable_info = yaml.safe_load(deliverable_data)
validate.validate_stable_branches(
deliverable_info,
'ocata',
warnings.append,
errors.append,
)
self.assertEqual(0, len(warnings))
self.assertEqual(0, len(errors))
def test_explicit_stable_branch_type_invalid(self):
deliverable_data = textwrap.dedent('''
stable-branch-type: unknown
releases:
- version: 1.5.0
projects:
- repo: openstack/automaton
hash: be2885f544637e6ee6139df7dc7bf937925804dd
branches:
- name: stable/ocata
location: 1.5.0
''')
warnings = []
errors = []
deliverable_info = yaml.safe_load(deliverable_data)
validate.validate_stable_branches(
deliverable_info,
'ocata',
warnings.append,
errors.append,
)
self.assertEqual(0, len(warnings))
self.assertEqual(1, len(errors))
def test_tagless_stable_branch_type_bad_location_type(self):
deliverable_data = textwrap.dedent('''
stable-branch-type: tagless
releases:
- version: 1.5.0
projects:
- repo: openstack/automaton
hash: be2885f544637e6ee6139df7dc7bf937925804dd
branches:
- name: stable/ocata
location: 1.5.0
''')
warnings = []
errors = []
deliverable_info = yaml.safe_load(deliverable_data)
validate.validate_stable_branches(
deliverable_info,
'ocata',
warnings.append,
errors.append,
)
self.assertEqual(0, len(warnings))
self.assertEqual(1, len(errors))
def test_tagless_stable_branch_type_bad_location_value(self):
deliverable_data = textwrap.dedent('''
stable-branch-type: tagless
releases:
- version: 1.5.0
projects:
- repo: openstack/automaton
hash: be2885f544637e6ee6139df7dc7bf937925804dd
branches:
- name: stable/ocata
location:
openstack/automaton: 1.5.0
''')
warnings = []
errors = []
deliverable_info = yaml.safe_load(deliverable_data)
validate.validate_stable_branches(
deliverable_info,
'ocata',
warnings.append,
errors.append,
)
self.assertEqual(0, len(warnings))
self.assertEqual(1, len(errors))
def test_tagless_stable_branch_type(self):
deliverable_data = textwrap.dedent('''
stable-branch-type: tagless
releases:
- version: 1.5.0
projects:
- repo: openstack/automaton
hash: be2885f544637e6ee6139df7dc7bf937925804dd
branches:
- name: stable/ocata
location:
openstack/automaton: be2885f544637e6ee6139df7dc7bf937925804dd
''')
warnings = []
errors = []
deliverable_info = yaml.safe_load(deliverable_data)
validate.validate_stable_branches(
deliverable_info,
'ocata',
warnings.append,
errors.append,
)
self.assertEqual(0, len(warnings))
self.assertEqual(0, len(errors))
class TestValidateFeatureBranches(base.BaseTestCase):