Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(971)

Unified Diff: presubmit_support.py

Issue 1927773002: Reland Implement owners check in presubmit for Gerrit. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@P300
Patch Set: gerrit fix Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « presubmit_canned_checks.py ('k') | tests/presubmit_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: presubmit_support.py
diff --git a/presubmit_support.py b/presubmit_support.py
index 07cf7b24b088993f31f390a84bdafff4cc0acf0d..2c4d0eb7694c416c7296f54c7d22a33abb7e708e 100755
--- a/presubmit_support.py
+++ b/presubmit_support.py
@@ -197,6 +197,73 @@ class _MailTextResult(_PresubmitResult):
super(_MailTextResult, self).__init__()
raise NotImplementedError()
+class GerritAccessor(object):
+ """Limited Gerrit functionality for canned presubmit checks to work.
+
+ To avoid excessive Gerrit calls, caches the results.
+ """
+
+ def __init__(self, host):
+ self.host = host
+ self.cache = {}
+
+ def _FetchChangeDetail(self, issue):
+ # Separate function to be easily mocked in tests.
+ return gerrit_util.GetChangeDetail(
+ self.host, str(issue),
+ ['ALL_REVISIONS', 'DETAILED_LABELS'])
+
+ def GetChangeInfo(self, issue):
+ """Returns labels and all revisions (patchsets) for this issue.
+
+ The result is a dictionary according to Gerrit REST Api.
+ https://gerrit-review.googlesource.com/Documentation/rest-api.html
+
+ However, API isn't very clear what's inside, so see tests for example.
+ """
+ assert issue
+ cache_key = int(issue)
+ if cache_key not in self.cache:
+ self.cache[cache_key] = self._FetchChangeDetail(issue)
+ return self.cache[cache_key]
+
+ def GetChangeDescription(self, issue, patchset=None):
+ """If patchset is none, fetches current patchset."""
+ info = self.GetChangeInfo(issue)
+ # info is a reference to cache. We'll modify it here adding description to
+ # it to the right patchset, if it is not yet there.
+
+ # Find revision info for the patchset we want.
+ if patchset is not None:
+ for rev, rev_info in info['revisions'].iteritems():
+ if str(rev_info['_number']) == str(patchset):
+ break
+ else:
+ raise Exception('patchset %s doesn\'t exist in issue %s' % (
+ patchset, issue))
+ else:
+ rev = info['current_revision']
+ rev_info = info['revisions'][rev]
+
+ # Updates revision info, which is part of cached issue info.
+ if 'real_description' not in rev_info:
+ rev_info['real_description'] = (
+ gerrit_util.GetChangeDescriptionFromGitiles(
+ rev_info['fetch']['http']['url'], rev))
+ return rev_info['real_description']
+
+ def GetChangeOwner(self, issue):
+ return self.GetChangeInfo(issue)['owner']['email']
+
+ def GetChangeReviewers(self, issue, approving_only=True):
+ # Gerrit has 'approved' sub-section, but it only lists 1 approver.
+ # So, if we look only for approvers, we have to look at all anyway.
+ # Also, assume LGTM means Code-Review label == 2. Other configurations
+ # aren't supported.
+ return [r['email']
+ for r in self.GetChangeInfo(issue)['labels']['Code-Review']['all']
+ if not approving_only or '2' == str(r.get('value', 0))]
+
class OutputApi(object):
"""An instance of OutputApi gets passed to presubmit scripts so that they
@@ -265,7 +332,7 @@ class InputApi(object):
)
def __init__(self, change, presubmit_path, is_committing,
- rietveld_obj, verbose, dry_run=None):
+ rietveld_obj, verbose, gerrit_obj=None, dry_run=None):
"""Builds an InputApi object.
Args:
@@ -273,12 +340,15 @@ class InputApi(object):
presubmit_path: The path to the presubmit script being processed.
is_committing: True if the change is about to be committed.
rietveld_obj: rietveld.Rietveld client object
+ gerrit_obj: provides basic Gerrit codereview functionality.
+ dry_run: if true, some Checks will be skipped.
"""
# Version number of the presubmit_support script.
self.version = [int(x) for x in __version__.split('.')]
self.change = change
self.is_committing = is_committing
self.rietveld = rietveld_obj
+ self.gerrit = gerrit_obj
self.dry_run = dry_run
# TBD
self.host_url = 'http://codereview.chromium.org'
@@ -1349,16 +1419,19 @@ def DoPostUploadExecuter(change,
class PresubmitExecuter(object):
def __init__(self, change, committing, rietveld_obj, verbose,
- dry_run=None):
+ gerrit_obj=None, dry_run=None):
"""
Args:
change: The Change object.
committing: True if 'gcl commit' is running, False if 'gcl upload' is.
rietveld_obj: rietveld.Rietveld client object.
+ gerrit_obj: provides basic Gerrit codereview functionality.
+ dry_run: if true, some Checks will be skipped.
"""
self.change = change
self.committing = committing
self.rietveld = rietveld_obj
+ self.gerrit = gerrit_obj
self.verbose = verbose
self.dry_run = dry_run
@@ -1381,7 +1454,7 @@ class PresubmitExecuter(object):
# Load the presubmit script into context.
input_api = InputApi(self.change, presubmit_path, self.committing,
self.rietveld, self.verbose,
- dry_run=self.dry_run)
+ gerrit_obj=self.gerrit, dry_run=self.dry_run)
context = {}
try:
exec script_text in context
@@ -1424,6 +1497,7 @@ def DoPresubmitChecks(change,
default_presubmit,
may_prompt,
rietveld_obj,
+ gerrit_obj=None,
dry_run=None):
"""Runs all presubmit checks that apply to the files in the change.
@@ -1443,6 +1517,7 @@ def DoPresubmitChecks(change,
default_presubmit: A default presubmit script to execute in any case.
may_prompt: Enable (y/n) questions on warning or error.
rietveld_obj: rietveld.Rietveld object.
+ gerrit_obj: provides basic Gerrit codereview functionality.
dry_run: if true, some Checks will be skipped.
Warning:
@@ -1471,7 +1546,7 @@ def DoPresubmitChecks(change,
output.write("Warning, no PRESUBMIT.py found.\n")
results = []
executer = PresubmitExecuter(change, committing, rietveld_obj, verbose,
- dry_run=dry_run)
+ gerrit_obj, dry_run)
if default_presubmit:
if verbose:
output.write("Running default presubmit script.\n")
@@ -1696,7 +1771,8 @@ def main(argv=None):
parser.error('For unversioned directory, <files> is not optional.')
logging.info('Found %d file(s).' % len(files))
- rietveld_obj = None
+ rietveld_obj, gerrit_obj = None, None
+
if options.rietveld_url:
# The empty password is permitted: '' is not None.
if options.rietveld_private_key_file:
@@ -1718,21 +1794,12 @@ def main(argv=None):
logging.info('Got description: """\n%s\n"""', options.description)
if options.gerrit_url and options.gerrit_fetch:
- rietveld_obj = None
assert options.issue and options.patchset
- props = gerrit_util.GetChangeDetail(
- urlparse.urlparse(options.gerrit_url).netloc, str(options.issue),
- ['ALL_REVISIONS'])
- options.author = props['owner']['email']
- for rev, rev_info in props['revisions'].iteritems():
- if str(rev_info['_number']) == str(options.patchset):
- options.description = gerrit_util.GetChangeDescriptionFromGitiles(
- rev_info['fetch']['http']['url'], rev)
- break
- else:
- print >> sys.stderr, ('Patchset %d was not found in Gerrit issue %d' %
- options.patchset, options.issue)
- return 2
+ rietveld_obj = None
+ gerrit_obj = GerritAccessor(urlparse.urlparse(options.gerrit_url).netloc)
+ options.author = gerrit_obj.GetChangeOwner(options.issue)
+ options.description = gerrit_obj.GetChangeDescription(options.issue,
+ options.patchset)
logging.info('Got author: "%s"', options.author)
logging.info('Got description: """\n%s\n"""', options.description)
@@ -1754,6 +1821,7 @@ def main(argv=None):
options.default_presubmit,
options.may_prompt,
rietveld_obj,
+ gerrit_obj,
options.dry_run)
return not results.should_continue()
except NonexistantCannedCheckFilter, e:
« no previous file with comments | « presubmit_canned_checks.py ('k') | tests/presubmit_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698