Add update_reviews tool
This tool lifts all of the user's reviews that they have -2d for a project using the gerrit REST API. Change-Id: Ie955417e1d3d068b07a445649dec61c14861f520
This commit is contained in:
parent
6d7348b07f
commit
3c369591e9
18
README.rst
18
README.rst
@ -784,3 +784,21 @@ Example::
|
||||
|
||||
Add a 'You won!' comment (with subject line 'Winner') to Launchpad
|
||||
bugs #1000000 and #2000000
|
||||
|
||||
|
||||
update_reviews
|
||||
--------------
|
||||
|
||||
Lift your -2 reviews from a project. Use this after the stable branch has been
|
||||
created and the project is ready for accept new features.
|
||||
|
||||
This tool uses the Gerrit REST API. So you need to provide your username and
|
||||
password somehow. You probably already have a .gertty.yaml, if not make one.
|
||||
|
||||
Example::
|
||||
|
||||
update_reviews oslo.config
|
||||
|
||||
The tool looks for all of the changes in the project that you have a -2 vote on
|
||||
and changes your vote to 0, with the message "This project is now open for new
|
||||
features."
|
||||
|
31
releasetools/cmds/update_reviews.py
Normal file
31
releasetools/cmds/update_reviews.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from releasetools import update_reviews
|
||||
|
||||
|
||||
def updating_review_cb(r):
|
||||
print('Updating %s in %s' % (r['change_id'], r['project']))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# Get urllib3 to shut up.
|
||||
warnings.simplefilter('ignore', Warning)
|
||||
|
||||
project = sys.argv[1]
|
||||
u_r = update_reviews.UpdateReviews(project,
|
||||
updating_review_cb=updating_review_cb)
|
||||
u_r.update_my_reviews()
|
119
releasetools/tests/test_update_reviews.py
Normal file
119
releasetools/tests/test_update_reviews.py
Normal file
@ -0,0 +1,119 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
|
||||
import mock
|
||||
from oslotest import base
|
||||
from oslotest import mockpatch
|
||||
import requests_mock
|
||||
|
||||
from releasetools import update_reviews
|
||||
|
||||
|
||||
class TestUpdateReviews(base.BaseTestCase):
|
||||
|
||||
def _patch_conf(self):
|
||||
fake_conf = {
|
||||
'username': mock.sentinel.username,
|
||||
'password': mock.sentinel.password,
|
||||
'url': 'https://review.openstack.org/',
|
||||
}
|
||||
po = mockpatch.PatchObject(update_reviews, '_read_config',
|
||||
return_value=fake_conf)
|
||||
self.useFixture(po)
|
||||
|
||||
def test_update_my_reviews(self):
|
||||
self._patch_conf()
|
||||
u_r = update_reviews.UpdateReviews(mock.sentinel.project)
|
||||
|
||||
sample_reviews = [mock.sentinel.r1, mock.sentinel.r2]
|
||||
po = mockpatch.PatchObject(u_r, '_list_my_reviews',
|
||||
return_value=sample_reviews)
|
||||
list_my_reviews_mock = self.useFixture(po).mock
|
||||
|
||||
update_review_mock = self.useFixture(
|
||||
mockpatch.PatchObject(u_r, '_update_review')).mock
|
||||
|
||||
u_r.update_my_reviews()
|
||||
|
||||
self.assertEqual(1, list_my_reviews_mock.call_count)
|
||||
exp_calls = [mock.call(mock.sentinel.r1), mock.call(mock.sentinel.r2)]
|
||||
self.assertEqual(exp_calls, update_review_mock.call_args_list)
|
||||
|
||||
def test_updating_review_callback(self):
|
||||
self._patch_conf()
|
||||
|
||||
cb = mock.Mock()
|
||||
u_r = update_reviews.UpdateReviews(mock.sentinel.project,
|
||||
updating_review_cb=cb)
|
||||
|
||||
sample_reviews = [mock.sentinel.r1, mock.sentinel.r2]
|
||||
po = mockpatch.PatchObject(u_r, '_list_my_reviews',
|
||||
return_value=sample_reviews)
|
||||
self.useFixture(po).mock
|
||||
|
||||
self.useFixture(
|
||||
mockpatch.PatchObject(u_r, '_update_review')).mock
|
||||
|
||||
u_r.update_my_reviews()
|
||||
|
||||
exp_calls = [mock.call(mock.sentinel.r1), mock.call(mock.sentinel.r2)]
|
||||
self.assertEqual(exp_calls, cb.call_args_list)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_list_my_reviews(self, m):
|
||||
self._patch_conf()
|
||||
u_r = update_reviews.UpdateReviews(mock.sentinel.project)
|
||||
|
||||
sample_result = []
|
||||
|
||||
gerrit_magic_prefix = ")]}'\n"
|
||||
sample_text = '%s%s' % (gerrit_magic_prefix, json.dumps(sample_result))
|
||||
m.get('https://review.openstack.org/a/changes/', text=sample_text)
|
||||
ret = u_r._list_my_reviews()
|
||||
self.assertEqual(sample_result, ret)
|
||||
exp_qs = {
|
||||
'q': ['project:%s branch:master status:open '
|
||||
'label:code-review=-2,self' % mock.sentinel.project],
|
||||
'o': ['current_revision'],
|
||||
}
|
||||
self.assertEqual(exp_qs, m.request_history[0].qs)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_update_review(self, m):
|
||||
self._patch_conf()
|
||||
u_r = update_reviews.UpdateReviews(mock.sentinel.project)
|
||||
|
||||
change_id = mock.sentinel.change_id
|
||||
revision_id = mock.sentinel.revision_id
|
||||
|
||||
url_base = 'https://review.openstack.org/'
|
||||
url = ('%sa/changes/%s/revisions/%s/review' %
|
||||
(url_base, change_id, revision_id))
|
||||
m.post(url)
|
||||
|
||||
review = {
|
||||
'id': change_id,
|
||||
'current_revision': revision_id,
|
||||
}
|
||||
|
||||
u_r._update_review(review)
|
||||
|
||||
exp_req = {
|
||||
'message': 'This project is now open for new features.',
|
||||
'labels': {
|
||||
'Code-Review': 0,
|
||||
},
|
||||
}
|
||||
|
||||
self.assertEqual(exp_req, m.request_history[0].json())
|
82
releasetools/update_reviews.py
Normal file
82
releasetools/update_reviews.py
Normal file
@ -0,0 +1,82 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import os.path
|
||||
|
||||
import pbr.version
|
||||
import requests
|
||||
from requests import auth
|
||||
import yaml
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo('update_reviews').version_string()
|
||||
|
||||
|
||||
def _read_config():
|
||||
data = yaml.safe_load(open(os.path.expanduser('~/.gertty.yaml')))
|
||||
servers = data['servers']
|
||||
for s in servers:
|
||||
if s['name'] == 'openstack':
|
||||
return s
|
||||
|
||||
|
||||
class UpdateReviews(object):
|
||||
def __init__(self, project, user=None, password=None,
|
||||
updating_review_cb=lambda x: None):
|
||||
self.base_url = 'https://review.openstack.org/'
|
||||
|
||||
if not (user and password):
|
||||
config = _read_config()
|
||||
user = config['username']
|
||||
password = config['password']
|
||||
self.base_url = config['url']
|
||||
|
||||
self.auth = auth.HTTPDigestAuth(user, password)
|
||||
|
||||
self.project = project
|
||||
self.updating_review_cb = updating_review_cb
|
||||
|
||||
def _list_my_reviews(self):
|
||||
url = '%sa/changes/' % self.base_url
|
||||
branch = 'master'
|
||||
query = ('project:%s branch:%s status:open '
|
||||
'label:Code-Review=-2,self' % (self.project, branch))
|
||||
params = {'q': query, 'o': 'CURRENT_REVISION'}
|
||||
r = requests.get(url, params=params, auth=self.auth)
|
||||
r.raise_for_status()
|
||||
|
||||
# Note that the result is not JSON, it's got a leading line that needs
|
||||
# to be removed first.
|
||||
res_text = r.text
|
||||
(dummy_magic_prefix, dummy_nl, res_json) = res_text.partition('\n')
|
||||
return json.loads(res_json)
|
||||
|
||||
def _update_review(self, r):
|
||||
change_id = r['id']
|
||||
revision_id = r['current_revision']
|
||||
url = ('%sa/changes/%s/revisions/%s/review' %
|
||||
(self.base_url, change_id, revision_id))
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
payload = {
|
||||
'message': 'This project is now open for new features.',
|
||||
'labels': {
|
||||
'Code-Review': 0,
|
||||
},
|
||||
}
|
||||
r = requests.post(url, auth=self.auth, headers=headers, json=payload)
|
||||
r.raise_for_status()
|
||||
|
||||
def update_my_reviews(self):
|
||||
for r in self._list_my_reviews():
|
||||
self.updating_review_cb(r)
|
||||
self._update_review(r)
|
@ -36,3 +36,4 @@ console_scripts =
|
||||
send-mail = releasetools.cmds.mail:main
|
||||
dashboard = releasetools.cmds.dashboard:main
|
||||
batch-stable-branches = releasetools.cmds.batch_create_stable_branches:main
|
||||
update-reviews = releasetools.cmds.update_reviews:main
|
||||
|
@ -10,3 +10,4 @@ testrepository>=0.0.18
|
||||
testscenarios>=0.4
|
||||
testtools>=1.4.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
requests-mock>=0.7.0 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user