add a validation rule to block new releases based on series status
We agreed at the PTG in Dublin that we would not allow releases from branches in 'extended maintenance' mode, so that leaves 'development' and 'maintenance'. Story: #2001852 Change-Id: I129bdd1c6103615179a7fb7918d37fd02ba0b914 Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
parent
481185dc20
commit
de52a1fefa
@ -875,6 +875,51 @@ def validate_new_releases_at_end(deliv, context):
|
|||||||
print('OK')
|
print('OK')
|
||||||
|
|
||||||
|
|
||||||
|
@applies_to_released
|
||||||
|
def validate_new_releases_in_open_series(deliv, context):
|
||||||
|
"New releases may only be added to open series."
|
||||||
|
|
||||||
|
if deliv.series_info.allows_releases:
|
||||||
|
print('{} has status {!r} and allows releases'.format(
|
||||||
|
deliv.series, deliv.series_info.status))
|
||||||
|
return
|
||||||
|
|
||||||
|
LOG.debug('%s has status %r and will not allow releases',
|
||||||
|
deliv.series, deliv.series_info.status)
|
||||||
|
|
||||||
|
# Remember which entries are new so we can verify that they
|
||||||
|
# appear at the end of the file.
|
||||||
|
new_releases = {}
|
||||||
|
|
||||||
|
for release in deliv.releases:
|
||||||
|
|
||||||
|
for project in release.projects:
|
||||||
|
|
||||||
|
if not gitutils.safe_clone_repo(context.workdir, project.repo.name,
|
||||||
|
project.hash, context):
|
||||||
|
continue
|
||||||
|
|
||||||
|
version_exists = gitutils.commit_exists(
|
||||||
|
context.workdir, project.repo.name, release.version,
|
||||||
|
)
|
||||||
|
if version_exists:
|
||||||
|
print('tag exists, skipping further validation')
|
||||||
|
continue
|
||||||
|
|
||||||
|
LOG.debug('Found new version {} for {}'.format(
|
||||||
|
release.version, project.repo))
|
||||||
|
new_releases[release.version] = release
|
||||||
|
|
||||||
|
if new_releases:
|
||||||
|
# The series is closed but there is a new release.
|
||||||
|
msg = ('series {} has status {!r} '
|
||||||
|
'and cannot have new releases tagged').format(
|
||||||
|
deliv.series, deliv.series_info.status)
|
||||||
|
context.error(msg)
|
||||||
|
else:
|
||||||
|
print('OK')
|
||||||
|
|
||||||
|
|
||||||
@applies_to_released
|
@applies_to_released
|
||||||
def validate_release_branch_membership(deliv, context):
|
def validate_release_branch_membership(deliv, context):
|
||||||
"Commits being tagged need to be on the right branch."
|
"Commits being tagged need to be on the right branch."
|
||||||
@ -1471,6 +1516,7 @@ def main():
|
|||||||
validate_existing_tags,
|
validate_existing_tags,
|
||||||
validate_version_numbers,
|
validate_version_numbers,
|
||||||
validate_new_releases_at_end,
|
validate_new_releases_at_end,
|
||||||
|
validate_new_releases_in_open_series,
|
||||||
validate_release_branch_membership,
|
validate_release_branch_membership,
|
||||||
validate_tarball_base,
|
validate_tarball_base,
|
||||||
validate_new_releases,
|
validate_new_releases,
|
||||||
|
@ -25,6 +25,7 @@ import weakref
|
|||||||
import pbr.version
|
import pbr.version
|
||||||
|
|
||||||
from openstack_releases import governance
|
from openstack_releases import governance
|
||||||
|
from openstack_releases import series_status
|
||||||
from openstack_releases import yamlutils
|
from openstack_releases import yamlutils
|
||||||
|
|
||||||
|
|
||||||
@ -330,6 +331,7 @@ class Branch(object):
|
|||||||
class Deliverable(object):
|
class Deliverable(object):
|
||||||
|
|
||||||
_governance_data = None
|
_governance_data = None
|
||||||
|
_series_status_data = None
|
||||||
|
|
||||||
def __init__(self, team, series, name, data):
|
def __init__(self, team, series, name, data):
|
||||||
self.team = team
|
self.team = team
|
||||||
@ -522,6 +524,12 @@ class Deliverable(object):
|
|||||||
def cycle_highlights(self):
|
def cycle_highlights(self):
|
||||||
return self._data.get('cycle-highlights', [])
|
return self._data.get('cycle-highlights', [])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def series_info(self):
|
||||||
|
if self._series_status_data is None:
|
||||||
|
self._series_status_data = series_status.SeriesStatus.default()
|
||||||
|
return self._series_status_data[self.series]
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.name == other.name
|
return self.name == other.name
|
||||||
|
|
||||||
|
@ -47,6 +47,10 @@ class Series(object):
|
|||||||
def eol_date(self):
|
def eol_date(self):
|
||||||
return self._data.get('eol-date', None)
|
return self._data.get('eol-date', None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def allows_releases(self):
|
||||||
|
return self.status in ('development', 'maintained')
|
||||||
|
|
||||||
|
|
||||||
class SeriesStatus(collections.abc.Mapping):
|
class SeriesStatus(collections.abc.Mapping):
|
||||||
|
|
||||||
@ -59,6 +63,12 @@ class SeriesStatus(collections.abc.Mapping):
|
|||||||
raw_data = cls._load_series_status_data(root_dir)
|
raw_data = cls._load_series_status_data(root_dir)
|
||||||
return cls(raw_data)
|
return cls(raw_data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls):
|
||||||
|
module_path = os.path.dirname(__file__)
|
||||||
|
root_dir = os.path.dirname(module_path)
|
||||||
|
return cls.from_directory(root_dir)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _load_series_status_data(root_dir):
|
def _load_series_status_data(root_dir):
|
||||||
filename = os.path.join(root_dir, 'deliverables', 'series_status.yaml')
|
filename = os.path.join(root_dir, 'deliverables', 'series_status.yaml')
|
||||||
|
@ -28,6 +28,7 @@ from openstack_releases import defaults
|
|||||||
from openstack_releases import deliverable
|
from openstack_releases import deliverable
|
||||||
from openstack_releases import gitutils
|
from openstack_releases import gitutils
|
||||||
from openstack_releases import processutils
|
from openstack_releases import processutils
|
||||||
|
from openstack_releases import series_status
|
||||||
from openstack_releases import yamlutils
|
from openstack_releases import yamlutils
|
||||||
|
|
||||||
|
|
||||||
@ -1022,6 +1023,141 @@ class TestValidateNewReleasesAtEnd(base.BaseTestCase):
|
|||||||
self.assertEqual(1, len(self.ctx.errors))
|
self.assertEqual(1, len(self.ctx.errors))
|
||||||
|
|
||||||
|
|
||||||
|
class TestValidateNewReleasesInOpenSeries(base.BaseTestCase):
|
||||||
|
|
||||||
|
_series_status_data = yamlutils.loads(textwrap.dedent('''
|
||||||
|
- name: rocky
|
||||||
|
status: development
|
||||||
|
initial-release: 2018-08-30
|
||||||
|
- name: queens
|
||||||
|
status: maintained
|
||||||
|
initial-release: 2018-02-28
|
||||||
|
- name: ocata
|
||||||
|
status: extended maintenance
|
||||||
|
initial-release: 2017-02-22
|
||||||
|
- name: newton
|
||||||
|
status: end of life
|
||||||
|
initial-release: 2016-10-06
|
||||||
|
eol-date: 2017-10-25
|
||||||
|
'''))
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.ctx = validate.ValidationContext()
|
||||||
|
gitutils.clone_repo(self.ctx.workdir, 'openstack/release-test')
|
||||||
|
self.series_status = series_status.SeriesStatus(
|
||||||
|
self._series_status_data)
|
||||||
|
self.useFixture(fixtures.MockPatch(
|
||||||
|
'openstack_releases.deliverable.Deliverable._series_status_data',
|
||||||
|
self.series_status,
|
||||||
|
))
|
||||||
|
|
||||||
|
def test_no_releases(self):
|
||||||
|
# When we initialize a new series, we won't have any release
|
||||||
|
# data. That's OK.
|
||||||
|
deliv = deliverable.Deliverable(
|
||||||
|
team='team',
|
||||||
|
series='rocky',
|
||||||
|
name='name',
|
||||||
|
data={
|
||||||
|
'artifact-link-mode': 'none',
|
||||||
|
'releases': []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
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))
|
||||||
|
|
||||||
|
def test_development(self):
|
||||||
|
deliv = deliverable.Deliverable(
|
||||||
|
team='team',
|
||||||
|
series='rocky',
|
||||||
|
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(0, len(self.ctx.errors))
|
||||||
|
|
||||||
|
def test_maintained(self):
|
||||||
|
deliv = deliverable.Deliverable(
|
||||||
|
team='team',
|
||||||
|
series='queens',
|
||||||
|
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(0, len(self.ctx.errors))
|
||||||
|
|
||||||
|
def test_extended_maintaintenance(self):
|
||||||
|
deliv = deliverable.Deliverable(
|
||||||
|
team='team',
|
||||||
|
series='ocata',
|
||||||
|
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_end_of_life(self):
|
||||||
|
deliv = deliverable.Deliverable(
|
||||||
|
team='team',
|
||||||
|
series='newton',
|
||||||
|
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))
|
||||||
|
|
||||||
|
|
||||||
class TestValidateVersionNumbers(base.BaseTestCase):
|
class TestValidateVersionNumbers(base.BaseTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user