diff --git a/openstack_releases/cmds/validate.py b/openstack_releases/cmds/validate.py index b50ca26c5d..067a96610d 100644 --- a/openstack_releases/cmds/validate.py +++ b/openstack_releases/cmds/validate.py @@ -298,17 +298,7 @@ def validate_series_final(deliv, context): print('OK') -@skip_existing_tags -@applies_to_released -def validate_series_eol(deliv, context): - "The EOL tag should be applied to the previous release." - - current_release = deliv.releases[-1] - - if not current_release.is_eol: - print('this rule only applies when tagging a series as end-of-life') - return - +def _require_tag_on_all_repos(deliv, current_release, eol_or_em, context): # The tag should be applied to all of the repositories for the # deliverable. actual_repos = set(p.repo.name for p in current_release.projects) @@ -317,9 +307,9 @@ def validate_series_eol(deliv, context): for extra in actual_repos.difference(expected_repos): error = True context.error( - 'EOL release %s includes repository %s ' + '%s release %s includes repository %s ' 'that is not in deliverable' % - (current_release.version, extra) + (eol_or_em, current_release.version, extra) ) for missing in expected_repos.difference(actual_repos): error = True @@ -332,6 +322,45 @@ def validate_series_eol(deliv, context): print('OK') +@skip_existing_tags +@applies_to_released +def validate_series_eol(deliv, context): + "The EOL tag should be applied to all repositories." + + current_release = deliv.releases[-1] + + if not current_release.is_eol: + print('this rule only applies when tagging a series as end-of-life') + return + + _require_tag_on_all_repos( + deliv, + current_release, + 'EOL', + context, + ) + + +@skip_existing_tags +@applies_to_released +def validate_series_em(deliv, context): + "The EM tag should be applied to the previous release." + + current_release = deliv.releases[-1] + + if not current_release.is_em: + print('this rule only applies when tagging ' + 'a series as extended-maintenance') + return + + _require_tag_on_all_repos( + deliv, + current_release, + 'extended maintenance', + context, + ) + + @skip_existing_tags @applies_to_current @applies_to_released @@ -885,6 +914,15 @@ def validate_version_numbers(deliv, context): release.version, deliv.series)) continue + if release.is_em: + LOG.debug('Found new EM tag {} for {}'.format( + release.version, deliv.name)) + if release.em_series != deliv.series: + context.error( + 'EM tag {} does not refer to the {} series.'.format( + release.version, deliv.series)) + continue + for project in release.projects: if not gitutils.safe_clone_repo(context.workdir, project.repo.name, @@ -1082,6 +1120,9 @@ def validate_new_releases_in_open_series(deliv, context): if release.is_eol: LOG.debug('Found new EOL tag {} for {}'.format( release.version, project.repo)) + elif release.is_em: + LOG.debug('Found new EM tag {} for {}'.format( + release.version, project.repo)) else: LOG.debug('Found new version {} for {}'.format( release.version, project.repo)) @@ -1715,6 +1756,7 @@ def main(): validate_series_final, validate_series_post_final, validate_series_eol, + validate_series_em, validate_branch_prefixes, validate_stable_branches, validate_feature_branches, diff --git a/openstack_releases/deliverable.py b/openstack_releases/deliverable.py index b6df94ed25..70b919977c 100644 --- a/openstack_releases/deliverable.py +++ b/openstack_releases/deliverable.py @@ -321,6 +321,16 @@ class Release(object): return self.version.rpartition('-')[0] return '' + @property + def is_em(self): + return self.version.endswith('-em') + + @property + def em_series(self): + if self.is_em: + return self.version.rpartition('-')[0] + return '' + def __eq__(self, other): return self.version == other.version diff --git a/openstack_releases/tests/test_validate.py b/openstack_releases/tests/test_validate.py index 4e9c5f0bf1..7a024fcfe3 100644 --- a/openstack_releases/tests/test_validate.py +++ b/openstack_releases/tests/test_validate.py @@ -1075,6 +1075,28 @@ class TestValidateNewReleasesInOpenSeries(base.BaseTestCase): self.assertEqual(0, len(self.ctx.warnings)) self.assertEqual(0, len(self.ctx.errors)) + def test_em_in_end_of_life(self): + deliv = deliverable.Deliverable( + team='team', + series='newton', + name='name', + data={ + 'artifact-link-mode': 'none', + 'releases': [ + {'version': 'newton-em', + 'projects': [ + {'repo': 'openstack/release-test', + 'hash': 'a26e6a2e8a5e321b2e3517dbb01a7b9a56a8bfd5', + 'tarball-base': 'openstack-release-test'}, + ]}, + ], + } + ) + validate.validate_new_releases_in_open_series(deliv, self.ctx) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + class TestValidateVersionNumbers(base.BaseTestCase): @@ -1153,6 +1175,27 @@ class TestValidateVersionNumbers(base.BaseTestCase): self.assertEqual(0, len(self.ctx.warnings)) self.assertEqual(0, len(self.ctx.errors)) + def test_em_valid_version(self): + deliv = deliverable.Deliverable( + team='team', + series='ocata', + name='name', + data={ + 'artifact-link-mode': 'none', + 'releases': [ + {'version': 'ocata-em', + 'projects': [ + {'repo': 'openstack/release-test', + 'hash': 'a26e6a2e8a5e321b2e3517dbb01a7b9a56a8bfd5'}, + ]} + ], + } + ) + validate.validate_version_numbers(deliv, self.ctx) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + def test_eol_wrong_branch(self): deliv = deliverable.Deliverable( team='team', @@ -1174,6 +1217,27 @@ class TestValidateVersionNumbers(base.BaseTestCase): self.assertEqual(0, len(self.ctx.warnings)) self.assertEqual(1, len(self.ctx.errors)) + def test_em_wrong_branch(self): + deliv = deliverable.Deliverable( + team='team', + series='ocata', + name='name', + data={ + 'artifact-link-mode': 'none', + 'releases': [ + {'version': 'newton-em', + 'projects': [ + {'repo': 'openstack/release-test', + 'hash': 'a26e6a2e8a5e321b2e3517dbb01a7b9a56a8bfd5'}, + ]} + ], + } + ) + validate.validate_version_numbers(deliv, self.ctx) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(1, len(self.ctx.errors)) + def test_no_releases(self): # When we initialize a new series, we won't have any release # data. That's OK. @@ -3057,6 +3121,140 @@ class TestValidateSeriesEOL(base.BaseTestCase): self.assertEqual(1, len(self.ctx.errors)) +class TestValidateSeriesEM(base.BaseTestCase): + + def setUp(self): + super().setUp() + self.tmpdir = self.useFixture(fixtures.TempDir()).path + self.ctx = validate.ValidationContext() + self.useFixture(fixtures.MonkeyPatch( + 'openstack_releases.cmds.validate.includes_new_tag', + mock.Mock(return_value=True), + )) + + def test_no_releases(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + ''')) + deliv = deliverable.Deliverable( + None, + defaults.RELEASE, + 'test', + deliverable_data, + ) + validate.validate_series_em( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_only_normal(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: 1.5.1 + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + ''')) + deliv = deliverable.Deliverable( + None, + defaults.RELEASE, + 'test', + deliverable_data, + ) + validate.validate_series_em( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_no_em(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: 1.5.1 + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + - version: 1.5.2 + projects: + - repo: openstack/automaton + hash: ce2885f544637e6ee6139df7dc7bf937925804dd + ''')) + deliv = deliverable.Deliverable( + None, + defaults.RELEASE, + 'test', + deliverable_data, + ) + validate.validate_series_em( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_em_ok(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: newton-em + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + ''')) + deliv = deliverable.Deliverable( + None, + 'newton', + 'test', + deliverable_data, + ) + validate.validate_series_em( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_em_missing_repo(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: newton-em + projects: + - repo: openstack/automaton + hash: ce2885f544637e6ee6139df7dc7bf937925804dd + repository-settings: + openstack/automaton: {} + openstack/release-test: {} + ''')) + deliv = deliverable.Deliverable( + None, + 'newton', + 'test', + deliverable_data, + ) + validate.validate_series_em( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(1, len(self.ctx.errors)) + + class TestValidatePostSeriesFinal(base.BaseTestCase): def setUp(self):