Merge "add support for storyboard bug and task trackers"
This commit is contained in:
@@ -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):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user