Introduce EOM tag validation
With the new TC resolution the community replaces Extended Maintenance with Unmaintained status [1]. To accomodate this, validator needs to be extended to accept <series>-eom tags that applies at the tip of the given stable/<series> branch and the new unmaintained/<series> branch will be cut from that tag. [1] https://governance.openstack.org/tc/resolutions/20230724-unmaintained-branches.html Change-Id: I606ba5e5f8fd783d6c3f83356ded27a46e8bac56
This commit is contained in:
parent
77e8ba577c
commit
ceb33ae249
@ -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,
|
||||
|
@ -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')
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user