diff --git a/openstack_releases/cmds/validate.py b/openstack_releases/cmds/validate.py index e0cdc18d20..4b9493a193 100644 --- a/openstack_releases/cmds/validate.py +++ b/openstack_releases/cmds/validate.py @@ -84,6 +84,7 @@ _USES_PREVER = set([ _VALID_BRANCH_PREFIXES = set([ 'stable', + 'unmaintained', 'feature', 'bugfix', ]) @@ -188,16 +189,18 @@ def skip_existing_tags(f): return decorated -def skip_em_eol_tags(f): +def skip_em_eom_eol_tags(f): @functools.wraps(f) def decorated(deliv, context): - em_or_eol = False + em_or_eom_or_eol = False for release in deliv.releases: - if '-em' in release.version or '-eol' in release.version: - print('Skipping rule for EM or EOL tagging.') - em_or_eol = True + if ('-em' in release.version or + '-eom' in release.version or + '-eol' in release.version): + print('Skipping rule for EM, EOM or EOL tagging.') + em_or_eom_or_eol = True break - if not em_or_eol: + if not em_or_eom_or_eol: return f(deliv, context) return decorated @@ -441,6 +444,28 @@ def validate_series_eol(deliv, context): ) +@skip_existing_tags +@applies_to_released +def validate_series_eom(deliv, context): + """The EOM tag should be applied to all repositories.""" + + current_release = deliv.releases[-1] + + if not current_release.is_eom: + print('this rule only applies when tagging a series as unmaintained') + return + + if len(deliv.branches) == 0: + context.error('only branched deliverables can be tagged EOM') + + _require_tag_on_all_repos( + deliv, + current_release, + 'EOM', + context, + ) + + @skip_existing_tags @applies_to_released def validate_series_em(deliv, context): @@ -488,7 +513,7 @@ def validate_series_em(deliv, context): (current_hash, previous_hash)) -@skip_em_eol_tags +@skip_em_eom_eol_tags def validate_bugtracker(deliv, context): "Does the bug tracker info link to something that exists?" lp_name = deliv.launchpad_id @@ -533,7 +558,7 @@ def validate_bugtracker(deliv, context): context.error('No launchpad or storyboard project given') -@skip_em_eol_tags +@skip_em_eom_eol_tags def validate_team(deliv, context): "Look for the team name in the governance data." try: @@ -548,7 +573,7 @@ def validate_team(deliv, context): print('owned by team {}'.format(deliv.team)) -@skip_em_eol_tags +@skip_em_eom_eol_tags def validate_release_notes(deliv, context): "Make sure the release notes page exists, if it is specified." notes_link = deliv.release_notes @@ -580,7 +605,7 @@ def validate_release_notes(deliv, context): print('{} OK'.format(link)) -@skip_em_eol_tags +@skip_em_eom_eol_tags def validate_model(deliv, context): "Require a valid release model" @@ -702,7 +727,7 @@ def get_release_type(deliv, repo, workdir): return ('python-service', False) -@skip_em_eol_tags +@skip_em_eom_eol_tags @skip_existing_tags @applies_to_released def validate_release_type(deliv, context): @@ -744,7 +769,7 @@ def validate_release_type(deliv, context): ) -@skip_em_eol_tags +@skip_em_eom_eol_tags @applies_to_released def validate_tarball_base(deliv, context): "Does tarball-base match the expected value?" @@ -798,7 +823,7 @@ def validate_tarball_base(deliv, context): sdist, expected)) -@skip_em_eol_tags +@skip_em_eom_eol_tags @applies_to_released def validate_build_sdist(deliv, context): "Can we build an sdist for a python project?" @@ -854,7 +879,7 @@ def validate_build_sdist(deliv, context): ) -@skip_em_eol_tags +@skip_em_eom_eol_tags @skip_existing_tags @applies_to_released def validate_pypi_readme(deliv, context): @@ -874,6 +899,10 @@ def validate_pypi_readme(deliv, context): print('skipping README validation for EOL tag {}'.format( latest_release.version)) return + if latest_release.is_eom: + print('skipping README validation for EOM tag {}'.format( + latest_release.version)) + return if latest_release.is_em: print('skipping README validation for EM tag {}'.format( latest_release.version)) @@ -908,7 +937,7 @@ def validate_pypi_readme(deliv, context): print('OK') -@skip_em_eol_tags +@skip_em_eom_eol_tags @skip_existing_tags @applies_to_released def validate_pypi_permissions(deliv, context): @@ -1120,6 +1149,20 @@ def validate_version_numbers(deliv, context): release.version, deliv.series)) continue + if release.is_eom: + LOG.debug('Found new EOM tag {} for {}'.format( + release.version, deliv.name)) + if deliv.is_independent: + context.warning( + 'EOM tag {} on independent deliverable, branch not validated'.format( + release.version)) + continue + if release.eom_series != deliv.series: + context.error( + 'EOM tag {} does not refer to the {} series.'.format( + release.version, deliv.series)) + continue + if release.is_em: LOG.debug('Found new EM tag {} for {}'.format( release.version, deliv.name)) @@ -1314,7 +1357,7 @@ def validate_new_releases_at_end(deliv, context): print('OK') -@skip_em_eol_tags +@skip_em_eom_eol_tags @skip_existing_tags @applies_to_released def validate_new_releases_in_open_series(deliv, context): @@ -1467,7 +1510,7 @@ def validate_release_branch_membership(deliv, context): prev_version[project.repo.name] = release.version -@skip_em_eol_tags +@skip_em_eom_eol_tags @applies_to_current @applies_to_released def validate_new_releases(deliv, context): @@ -2004,6 +2047,7 @@ def main(): validate_series_final, validate_pre_release_progression, validate_series_eol, + validate_series_eom, validate_series_em, validate_branch_prefixes, validate_stable_branches, diff --git a/openstack_releases/deliverable.py b/openstack_releases/deliverable.py index 1a16a7a91c..d1db677ce9 100644 --- a/openstack_releases/deliverable.py +++ b/openstack_releases/deliverable.py @@ -59,11 +59,12 @@ def _safe_semver(v): def _version_sort_key(release): """Return a value we can compare for sorting.""" - # NOTE(dhellmann): We want EOL and EM tags to sort last. This assumes we - # won't have more than 1000 major releases of anything, and I - # surely hope that is a safe assumption. + # NOTE(dhellmann): We want EOL, EOM and EM tags to sort last. This + # assumes we won't have more than 1000 major releases of anything, + # and I surely hope that is a safe assumption. version_string = release['version'] if version_string.endswith('-eol') or \ + version_string.endswith('-eom') or \ version_string.endswith('-em') or \ version_string.endswith('-last'): return _safe_semver('1000.0.0') @@ -335,6 +336,16 @@ class Release(object): return self.version.rpartition('-')[0] return '' + @property + def is_eom(self): + return self.version.endswith('-eom') + + @property + def eom_series(self): + if self.is_eom: + return self.version.rpartition('-')[0] + return '' + @property def is_em(self): return self.version.endswith('-em') diff --git a/openstack_releases/tests/test_validate.py b/openstack_releases/tests/test_validate.py index 413add7e63..f0d4eb43b2 100644 --- a/openstack_releases/tests/test_validate.py +++ b/openstack_releases/tests/test_validate.py @@ -989,6 +989,9 @@ class TestValidateNewReleasesInOpenSeries(base.BaseTestCase): - name: queens status: maintained initial-release: 2018-02-28 + - name: pike + status: unmaintained + initial-release: 2017-08-30 - name: ocata status: extended maintenance initial-release: 2017-02-22 @@ -1070,6 +1073,28 @@ class TestValidateNewReleasesInOpenSeries(base.BaseTestCase): self.assertEqual(0, len(self.ctx.warnings)) self.assertEqual(0, len(self.ctx.errors)) + def test_unmaintained(self): + deliv = deliverable.Deliverable( + team='team', + series='pike', + name='name', + data={ + 'artifact-link-mode': 'none', + 'releases': [ + {'version': '10.0.0', + '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(1, len(self.ctx.errors)) + def test_extended_maintaintenance(self): deliv = deliverable.Deliverable( team='team', @@ -1264,6 +1289,27 @@ class TestValidateVersionNumbers(base.BaseTestCase): self.assertEqual(0, len(self.ctx.warnings)) self.assertEqual(0, len(self.ctx.errors)) + def test_eom_valid_version(self): + deliv = deliverable.Deliverable( + team='team', + series='ocata', + name='name', + data={ + 'artifact-link-mode': 'none', + 'releases': [ + {'version': 'ocata-eom', + '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_em_valid_version(self): deliv = deliverable.Deliverable( team='team', @@ -1306,6 +1352,27 @@ class TestValidateVersionNumbers(base.BaseTestCase): self.assertEqual(0, len(self.ctx.warnings)) self.assertEqual(1, len(self.ctx.errors)) + def test_eom_wrong_branch(self): + deliv = deliverable.Deliverable( + team='team', + series='ocata', + name='name', + data={ + 'artifact-link-mode': 'none', + 'releases': [ + {'version': 'newton-eom', + '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_em_wrong_branch(self): deliv = deliverable.Deliverable( team='team', @@ -3492,6 +3559,176 @@ class TestValidateSeriesEOL(base.BaseTestCase): self.assertEqual(1, len(self.ctx.errors)) +class TestValidateSeriesEOM(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_eom( + 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_eom( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_no_eom(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_eom( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_eom_ok(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: 1.5.2 + projects: + - repo: openstack/automaton + hash: ce2885f544637e6ee6139df7dc7bf937925804dd + - version: newton-eom + projects: + - repo: openstack/automaton + hash: be2885f544637e6ee6139df7dc7bf937925804dd + branches: + - name: stable/newton + location: 1.5.2 + ''')) + deliv = deliverable.Deliverable( + None, + 'newton', + 'test', + deliverable_data, + ) + validate.validate_series_eom( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(0, len(self.ctx.errors)) + + def test_eom_missing_repo(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: newton-eom + projects: + - repo: openstack/automaton + hash: ce2885f544637e6ee6139df7dc7bf937925804dd + branches: + - name: stable/newton + location: 1.2.3 + repository-settings: + openstack/automaton: {} + openstack/release-test: {} + ''')) + deliv = deliverable.Deliverable( + None, + 'newton', + 'test', + deliverable_data, + ) + validate.validate_series_eom( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(1, len(self.ctx.errors)) + + def test_eom_branchless(self): + deliverable_data = yamlutils.loads(textwrap.dedent(''' + --- + team: Release Management + releases: + - version: newton-eom + projects: + - repo: openstack/automaton + hash: ce2885f544637e6ee6139df7dc7bf937925804dd + repository-settings: + openstack/automaton: {} + ''')) + deliv = deliverable.Deliverable( + None, + 'newton', + 'test', + deliverable_data, + ) + validate.validate_series_eom( + deliv, + self.ctx, + ) + self.ctx.show_summary() + self.assertEqual(0, len(self.ctx.warnings)) + self.assertEqual(1, len(self.ctx.errors)) + + class TestValidateSeriesEM(base.BaseTestCase): def setUp(self):