diff --git a/openstack_releases/cmds/validate.py b/openstack_releases/cmds/validate.py index 2950387067..141422317f 100644 --- a/openstack_releases/cmds/validate.py +++ b/openstack_releases/cmds/validate.py @@ -1055,6 +1055,104 @@ def validate_driverfixes_branches(deliverable_info, _require_gitreview(workdir, repo, mk_error) +def validate_branch_points(deliverable_info, + deliverable_name, + workdir, + mk_warning, + mk_error): + # Make sure the branch points given are on the expected branches. + + known_releases = { + r['version']: r + for r in deliverable_info.get('releases', []) + } + branch_mode = deliverable_info.get('stable-branch-type', 'std') + + for branch in deliverable_info.get('branches', []): + header('Validate Branch Points: {}'.format(branch['name'])) + try: + prefix, series = branch['name'].split('/') + except ValueError: + print('could not parse the branch name, skipping') + continue + + if prefix == 'feature': + print('rule does not apply to feature branches') + continue + + elif prefix == 'stable': + expected = set([ + 'master', + branch['name'], + ]) + else: + # driverfixes + expected = set([ + branch['name'], + 'stable/' + series, + ]) + + if prefix == 'stable' and branch_mode == 'std': + # location is a version string, so we need to build the + # map ourselves + print('using hashes from release {}'.format(branch['location'])) + release = known_releases[branch['location']] + location = { + p['repo']: p['hash'] + for p in release['projects'] + } + else: + location = branch['location'] + + for repo, hash in sorted(location.items()): + print('\n{}'.format(repo)) + existing_branches = sorted([ + (b.partition('/origin/')[-1] + if b.startswith('remotes/origin/') + else b) + for b in gitutils.get_branches(workdir, repo) + ]) + + # Remove the remote name prefix if it is present in the + # branch name. + containing = set( + c.partition('/')[-1] if c.startswith('origin/') else c + for c in gitutils.branches_containing( + workdir, repo, hash) + ) + + print('found {} on branches {} in {}'.format( + hash, containing, repo)) + + for missing in expected.difference(containing): + if missing not in existing_branches: + print('branch {} does not exist in {}, skipping'.format( + branch['name'], repo)) + continue + + if branch['name'] in existing_branches: + # The branch already exists but there is something + # wrong with the specification. This probably + # means someone tried to update the branch setting + # after creating the branch, so phrase the error + # message to reflect that. + mk_error( + '{} branch exists in {} and does not seem ' + 'to have been created from {}'.format( + branch['name'], repo, hash), + ) + else: + # The branch does not exist and the proposed point + # to create it is not on the expected source + # branch, so phrase the error message to reflect + # that. + mk_error( + 'commit {} is not on the {} branch ' + 'but it is listed as the branch point for ' + '{} to be created'.format( + hash, missing, branch['name'])) + + # if the branch already exists, the name is by definition valid # if the branch exists, the data in the map must match reality # @@ -1231,6 +1329,13 @@ def main(): mk_warning, mk_error, ) + validate_branch_points( + deliverable_info, + deliverable_name, + workdir, + mk_warning, + mk_error, + ) header('Summary') diff --git a/openstack_releases/tests/test_validate.py b/openstack_releases/tests/test_validate.py index 51975ae067..382d08fb5f 100644 --- a/openstack_releases/tests/test_validate.py +++ b/openstack_releases/tests/test_validate.py @@ -2418,3 +2418,84 @@ class TestGuessDeliverableType(base.BaseTestCase): {}, ), ) + + +class TestValidateBranchPoints(base.BaseTestCase): + + def setUp(self): + super(TestValidateBranchPoints, self).setUp() + self.tmpdir = self.useFixture(fixtures.TempDir()).path + gitutils.clone_repo(self.tmpdir, 'openstack/release-test') + + def test_branch_does_not_exist(self): + deliverable_data = textwrap.dedent(''' + releases: + - version: 0.0.3 + projects: + - repo: openstack/release-test + hash: 0cd17d1ee3b9284d36b2a0d370b49a6f0bbb9660 + branches: + - name: stable/ocata + location: 0.0.3 + ''') + warnings = [] + errors = [] + deliverable_info = yamlutils.loads(deliverable_data) + validate.validate_branch_points( + deliverable_info, + 'name', + self.tmpdir, + warnings.append, + errors.append, + ) + self.assertEqual(0, len(warnings)) + self.assertEqual(0, len(errors)) + + def test_branch_is_correct(self): + deliverable_data = textwrap.dedent(''' + releases: + - version: 0.8.0 + projects: + - repo: openstack/release-test + hash: a26e6a2e8a5e321b2e3517dbb01a7b9a56a8bfd5 + branches: + - name: stable/newton + location: 0.8.0 + ''') + warnings = [] + errors = [] + deliverable_info = yamlutils.loads(deliverable_data) + validate.validate_branch_points( + deliverable_info, + 'name', + self.tmpdir, + warnings.append, + errors.append, + ) + self.assertEqual(0, len(warnings)) + self.assertEqual(0, len(errors)) + + def test_branch_moved(self): + deliverable_data = textwrap.dedent(''' + releases: + - version: 0.12.0 + projects: + - repo: openstack/release-test + hash: a26e6a2e8a5e321b2e3517dbb01a7b9a56a8bfd5 + branches: + - name: stable/meiji + location: 0.12.0 # this comes after the meiji branch + # was created at 0.0.2 + ''') + warnings = [] + errors = [] + deliverable_info = yamlutils.loads(deliverable_data) + validate.validate_branch_points( + deliverable_info, + 'name', + self.tmpdir, + warnings.append, + errors.append, + ) + self.assertEqual(0, len(warnings)) + self.assertEqual(1, len(errors))