add support for storyboard bug and task trackers
Some projects use storyboard instead of launchpad, so look for one or the other and try to do some basic validation of the storyboard project ID. Change-Id: I94a9507969bcc8bbe2358132761c0935d94cadaa Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
parent
50dd831793
commit
d5840915a6
@ -247,7 +247,8 @@ For deliverable set of projects, we use one YAML file per release
|
|||||||
series to hold all of the metadata for all releases and branches of
|
series to hold all of the metadata for all releases and branches of
|
||||||
that deliverable. For each deliverable, we need to track:
|
that deliverable. For each deliverable, we need to track:
|
||||||
|
|
||||||
* the launchpad project name (such as ``oslo.config``)
|
* the launchpad project name (such as ``oslo.config``) or storyboard
|
||||||
|
project id (such as ``760``)
|
||||||
* the series (Kilo, Liberty, etc.)
|
* the series (Kilo, Liberty, etc.)
|
||||||
* the release model being used
|
* the release model being used
|
||||||
* for each repository
|
* for each repository
|
||||||
@ -280,6 +281,11 @@ The top level of a deliverable file is a mapping with keys:
|
|||||||
|
|
||||||
``launchpad``
|
``launchpad``
|
||||||
The slug name of the launchpad project, suitable for use in URLs.
|
The slug name of the launchpad project, suitable for use in URLs.
|
||||||
|
(Not needed for projects using storyboard.)
|
||||||
|
|
||||||
|
``storyboard``
|
||||||
|
The ID of the storyboard project, suitable for use in URLs and API
|
||||||
|
calls. (Not needed for projects using launchpad.)
|
||||||
|
|
||||||
``release-notes``
|
``release-notes``
|
||||||
The URL or URLs to the published release notes for the deliverable
|
The URL or URLs to the published release notes for the deliverable
|
||||||
|
@ -142,13 +142,10 @@ def validate_series_first(deliverable_info, series_name,
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_launchpad(deliverable_info, mk_warning, mk_error):
|
def validate_bugtracker(deliverable_info, mk_warning, mk_error):
|
||||||
"Look for the launchpad project"
|
"Look for the bugtracker info"
|
||||||
try:
|
if 'launchpad' in deliverable_info:
|
||||||
lp_name = deliverable_info['launchpad']
|
lp_name = deliverable_info['launchpad']
|
||||||
except KeyError:
|
|
||||||
mk_error('No launchpad project given')
|
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
lp_resp = requests.get('https://api.launchpad.net/1.0/' + lp_name)
|
lp_resp = requests.get('https://api.launchpad.net/1.0/' + lp_name)
|
||||||
except requests.exceptions.ConnectionError as e:
|
except requests.exceptions.ConnectionError as e:
|
||||||
@ -158,6 +155,35 @@ def validate_launchpad(deliverable_info, mk_warning, mk_error):
|
|||||||
else:
|
else:
|
||||||
if (lp_resp.status_code // 100) == 4:
|
if (lp_resp.status_code // 100) == 4:
|
||||||
mk_error('Launchpad project %s does not exist' % lp_name)
|
mk_error('Launchpad project %s does not exist' % lp_name)
|
||||||
|
elif 'storyboard' in deliverable_info:
|
||||||
|
try:
|
||||||
|
sb_id = int(deliverable_info['storyboard'])
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
mk_error('Invalid storyboard ID, must be a number: %s' %
|
||||||
|
deliverable_info['storyboard'])
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
projects_resp = requests.get(
|
||||||
|
'https://storyboard.openstack.org/api/v1/projects'
|
||||||
|
)
|
||||||
|
except requests.exceptions.ConnectionError as e:
|
||||||
|
# The flakey Launchpad API failed. Don't punish the user for that.
|
||||||
|
mk_warning('Could not verify storyboard project %s (%s)' %
|
||||||
|
(sb_id, e))
|
||||||
|
else:
|
||||||
|
if (projects_resp.status_code // 100) == 4:
|
||||||
|
mk_warning(
|
||||||
|
'Could not verify storyboard project, API call failed.'
|
||||||
|
)
|
||||||
|
for project in projects_resp.json():
|
||||||
|
if sb_id == project.get('id'):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
mk_error(
|
||||||
|
'Did not find a storyboard project with ID %s' % sb_id
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
mk_error('No launchpad or storyboard project given')
|
||||||
|
|
||||||
|
|
||||||
def validate_team(deliverable_info, team_data, mk_warning, mk_error):
|
def validate_team(deliverable_info, team_data, mk_warning, mk_error):
|
||||||
@ -689,7 +715,7 @@ def main():
|
|||||||
print('ERROR: {}'.format(msg))
|
print('ERROR: {}'.format(msg))
|
||||||
errors.append('{}: {}'.format(filename, msg))
|
errors.append('{}: {}'.format(filename, msg))
|
||||||
|
|
||||||
validate_launchpad(deliverable_info, mk_warning, mk_error)
|
validate_bugtracker(deliverable_info, mk_warning, mk_error)
|
||||||
validate_team(deliverable_info, team_data, mk_warning, mk_error)
|
validate_team(deliverable_info, team_data, mk_warning, mk_error)
|
||||||
validate_release_notes(deliverable_info, mk_warning, mk_error)
|
validate_release_notes(deliverable_info, mk_warning, mk_error)
|
||||||
validate_type(deliverable_info, mk_warning, mk_error)
|
validate_type(deliverable_info, mk_warning, mk_error)
|
||||||
|
@ -26,12 +26,12 @@ from openstack_releases import defaults
|
|||||||
from openstack_releases.cmds import validate
|
from openstack_releases.cmds import validate
|
||||||
|
|
||||||
|
|
||||||
class TestValidateLaunchpad(base.BaseTestCase):
|
class TestValidateBugTracker(base.BaseTestCase):
|
||||||
|
|
||||||
def test_no_launchpad_name(self):
|
def test_no_tracker(self):
|
||||||
warnings = []
|
warnings = []
|
||||||
errors = []
|
errors = []
|
||||||
validate.validate_launchpad(
|
validate.validate_bugtracker(
|
||||||
{},
|
{},
|
||||||
warnings.append,
|
warnings.append,
|
||||||
errors.append,
|
errors.append,
|
||||||
@ -40,11 +40,11 @@ class TestValidateLaunchpad(base.BaseTestCase):
|
|||||||
self.assertEqual(1, len(errors))
|
self.assertEqual(1, len(errors))
|
||||||
|
|
||||||
@mock.patch('requests.get')
|
@mock.patch('requests.get')
|
||||||
def test_invalid_launchpad_name(self, get):
|
def test_launchpad_invalid_name(self, get):
|
||||||
get.return_value = mock.Mock(status_code=404)
|
get.return_value = mock.Mock(status_code=404)
|
||||||
warnings = []
|
warnings = []
|
||||||
errors = []
|
errors = []
|
||||||
validate.validate_launchpad(
|
validate.validate_bugtracker(
|
||||||
{'launchpad': 'nonsense-name'},
|
{'launchpad': 'nonsense-name'},
|
||||||
warnings.append,
|
warnings.append,
|
||||||
errors.append,
|
errors.append,
|
||||||
@ -53,11 +53,11 @@ class TestValidateLaunchpad(base.BaseTestCase):
|
|||||||
self.assertEqual(1, len(errors))
|
self.assertEqual(1, len(errors))
|
||||||
|
|
||||||
@mock.patch('requests.get')
|
@mock.patch('requests.get')
|
||||||
def test_valid_launchpad_name(self, get):
|
def test_launchpad_valid_name(self, get):
|
||||||
get.return_value = mock.Mock(status_code=200)
|
get.return_value = mock.Mock(status_code=200)
|
||||||
warnings = []
|
warnings = []
|
||||||
errors = []
|
errors = []
|
||||||
validate.validate_launchpad(
|
validate.validate_bugtracker(
|
||||||
{'launchpad': 'oslo.config'},
|
{'launchpad': 'oslo.config'},
|
||||||
warnings.append,
|
warnings.append,
|
||||||
errors.append,
|
errors.append,
|
||||||
@ -71,7 +71,7 @@ class TestValidateLaunchpad(base.BaseTestCase):
|
|||||||
get.side_effect = requests.exceptions.ConnectionError('testing')
|
get.side_effect = requests.exceptions.ConnectionError('testing')
|
||||||
warnings = []
|
warnings = []
|
||||||
errors = []
|
errors = []
|
||||||
validate.validate_launchpad(
|
validate.validate_bugtracker(
|
||||||
{'launchpad': 'oslo.config'},
|
{'launchpad': 'oslo.config'},
|
||||||
warnings.append,
|
warnings.append,
|
||||||
errors.append,
|
errors.append,
|
||||||
@ -79,6 +79,79 @@ class TestValidateLaunchpad(base.BaseTestCase):
|
|||||||
self.assertEqual(1, len(warnings))
|
self.assertEqual(1, len(warnings))
|
||||||
self.assertEqual(0, len(errors))
|
self.assertEqual(0, len(errors))
|
||||||
|
|
||||||
|
@mock.patch('requests.get')
|
||||||
|
def test_storyboard_valid_id(self, get):
|
||||||
|
get.return_value = mock.Mock(status_code=200)
|
||||||
|
get.return_value.json.return_value = [
|
||||||
|
{
|
||||||
|
"name": "openstack-infra/storyboard",
|
||||||
|
"created_at": "2014-03-12T17:52:19+00:00",
|
||||||
|
"is_active": True,
|
||||||
|
"updated_at": None,
|
||||||
|
"autocreate_branches": False,
|
||||||
|
"repo_url": None,
|
||||||
|
"id": 456,
|
||||||
|
"description": "OpenStack Task Tracking API",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "openstack-infra/shade",
|
||||||
|
"created_at": "2015-01-07T20:56:27+00:00",
|
||||||
|
"is_active": True,
|
||||||
|
"updated_at": None,
|
||||||
|
"autocreate_branches": False,
|
||||||
|
"repo_url": None,
|
||||||
|
"id": 760,
|
||||||
|
"description": "Client library for OpenStack...",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
warnings = []
|
||||||
|
errors = []
|
||||||
|
validate.validate_bugtracker(
|
||||||
|
{'storyboard': '760'},
|
||||||
|
warnings.append,
|
||||||
|
errors.append,
|
||||||
|
)
|
||||||
|
self.assertEqual(0, len(warnings))
|
||||||
|
self.assertEqual(0, len(errors))
|
||||||
|
|
||||||
|
@mock.patch('requests.get')
|
||||||
|
def test_storyboard_invalid_id(self, get):
|
||||||
|
get.return_value = mock.Mock(status_code=200)
|
||||||
|
warnings = []
|
||||||
|
errors = []
|
||||||
|
validate.validate_bugtracker(
|
||||||
|
{'storyboard': 'name-not-id'},
|
||||||
|
warnings.append,
|
||||||
|
errors.append,
|
||||||
|
)
|
||||||
|
self.assertEqual(0, len(warnings))
|
||||||
|
self.assertEqual(1, len(errors))
|
||||||
|
|
||||||
|
@mock.patch('requests.get')
|
||||||
|
def test_storyboard_no_such_project(self, get):
|
||||||
|
get.return_value = mock.Mock(status_code=200)
|
||||||
|
get.return_value.json.return_value = [
|
||||||
|
{
|
||||||
|
"name": "openstack-infra/storyboard",
|
||||||
|
"created_at": "2014-03-12T17:52:19+00:00",
|
||||||
|
"is_active": True,
|
||||||
|
"updated_at": None,
|
||||||
|
"autocreate_branches": False,
|
||||||
|
"repo_url": None,
|
||||||
|
"id": 456,
|
||||||
|
"description": "OpenStack Task Tracking API",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
warnings = []
|
||||||
|
errors = []
|
||||||
|
validate.validate_bugtracker(
|
||||||
|
{'storyboard': '-760'},
|
||||||
|
warnings.append,
|
||||||
|
errors.append,
|
||||||
|
)
|
||||||
|
self.assertEqual(0, len(warnings))
|
||||||
|
self.assertEqual(1, len(errors))
|
||||||
|
|
||||||
|
|
||||||
class TestValidateTeam(base.BaseTestCase):
|
class TestValidateTeam(base.BaseTestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user