diff --git a/openstack_releases/cmds/validate.py b/openstack_releases/cmds/validate.py index 3d5de8ef29..c9dbb49076 100644 --- a/openstack_releases/cmds/validate.py +++ b/openstack_releases/cmds/validate.py @@ -251,6 +251,40 @@ def validate_series_final(deliv, context): print('OK') +@applies_to_current +@applies_to_released +@applies_to_cycle +def validate_series_post_final(deliv, context): + "After a final release, releases should not use pre-release versions." + + releases = deliv.releases + if len(releases) < 2: + # We only have to check this when the first release is being + # applied in the file. + print('this rule only applies if a series has multiple releases') + return + + current_release = releases[-1] + + if not current_release.is_pre_release_version: + print('this rule only applies if the new ' + 'release has a pre-release version') + return + + # If there is a final release version in any of the previous + # releases, report the error. + for previous_release in releases[-2::-1]: + if not previous_release.is_pre_release_version: + context.error( + '{} uses a pre-release version number ' + 'after a final release version was tagged as {}'.format( + current_release.version, previous_release.version) + ) + return + + print('OK') + + def validate_bugtracker(deliv, context): "Does the bug tracker info link to something that exists?" lp_name = deliv.launchpad_id @@ -1443,6 +1477,7 @@ def main(): validate_series_open, validate_series_first, validate_series_final, + validate_series_post_final, validate_branch_prefixes, validate_stable_branches, validate_feature_branches, diff --git a/openstack_releases/deliverable.py b/openstack_releases/deliverable.py index 2b47e560ce..5956d686b3 100644 --- a/openstack_releases/deliverable.py +++ b/openstack_releases/deliverable.py @@ -284,6 +284,14 @@ class Release(object): def is_release_candidate(self): return 'rc' in self.version + @property + def is_pre_release_version(self): + return ( + 'rc' in self.version + or 'a' in self.version + or 'b' in self.version + ) + 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 4df3727dbd..6190cdda4a 100644 --- a/openstack_releases/tests/test_validate.py +++ b/openstack_releases/tests/test_validate.py @@ -2790,6 +2790,173 @@ class TestValidateSeriesFinal(base.BaseTestCase): self.assertEqual(1, len(self.ctx.errors)) +class TestValidatePostSeriesFinal(base.BaseTestCase): + + def setUp(self): + super().setUp() + self.tmpdir = self.useFixture(fixtures.TempDir()).path + self.ctx = validate.ValidationContext() + + 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_post_final( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_only_rc(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: 1.5.1.0rc1 + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + ''')) + deliv = deliverable.Deliverable( + None, + defaults.RELEASE, + 'test', + deliverable_data, + ) + validate.validate_series_post_final( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_no_rc(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_post_final( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_final_follows_rc(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: 1.5.0.0rc1 + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + - version: 1.5.0 + projects: + - repo: openstack/automaton + hash: ce2885f544637e6ee6139df7dc7bf937925804dd + ''')) + deliv = deliverable.Deliverable( + None, + defaults.RELEASE, + 'test', + deliverable_data, + ) + validate.validate_series_post_final( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_final_follows_multiple_rc(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: 1.5.0.0rc1 + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + - version: 1.5.0.0rc2 + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + - version: 1.5.0 + projects: + - repo: openstack/automaton + hash: ce2885f544637e6ee6139df7dc7bf937925804dd + ''')) + deliv = deliverable.Deliverable( + None, + defaults.RELEASE, + 'test', + deliverable_data, + ) + validate.validate_series_post_final( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_rc_follows_final(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: 1.5.0 + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + - version: 1.5.0.0rc1 + projects: + - repo: openstack/automaton + hash: ce2885f544637e6ee6139df7dc7bf937925804dd + ''')) + deliv = deliverable.Deliverable( + None, + defaults.RELEASE, + 'test', + deliverable_data, + ) + validate.validate_series_post_final( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(1, len(self.ctx.errors)) + + class TestValidateBranchPoints(base.BaseTestCase): def setUp(self):