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

Unified Diff: presubmit_canned_checks.py

Issue 6730020: Clean up the parsing of approvals for OWNERS checks. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: rebase to head Created 9 years, 9 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 | « no previous file | tests/presubmit_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: presubmit_canned_checks.py
diff --git a/presubmit_canned_checks.py b/presubmit_canned_checks.py
index 25a3460212422783dde6e84a8752bb32ee5e5cbe..01ae510bb16968e8bcbc6c69452cb8867e40770d 100644
--- a/presubmit_canned_checks.py
+++ b/presubmit_canned_checks.py
@@ -629,35 +629,37 @@ def CheckBuildbotPendingBuilds(input_api, output_api, url, max_pendings,
return []
-def CheckOwners(input_api, output_api, email_regexp=None,
- source_file_filter=None):
- affected_files = set([f.LocalPath() for f in
- input_api.change.AffectedFiles(source_file_filter)])
- owners_db = input_api.owners_db
- if email_regexp:
- owners_db.email_regexp = input_api.re.compile(email_regexp)
-
- if input_api.is_committing and input_api.tbr:
- return [output_api.PresubmitNotifyResult(
- '--tbr was specified, skipping OWNERS check')]
- elif input_api.is_committing:
- approvers = _Approvers(input_api, owners_db.email_regexp)
- missing_files = owners_db.files_not_covered_by(affected_files, approvers)
- if missing_files:
- return [output_api.PresubmitError('Missing LGTM from an OWNER for: %s' %
- ','.join(missing_files))]
- return []
- elif input_api.change.tags.get('R'):
+def CheckOwners(input_api, output_api, source_file_filter=None):
+ if not input_api.is_committing:
return []
+ if input_api.tbr:
+ return [output_api.PresubmitNotifyResult('--tbr was specified, '
+ 'skipping OWNERS check')]
+ if not input_api.change.issue:
+ return [output_api.PresubmitError('Change not uploaded for review')]
- suggested_reviewers = owners_db.reviewers_for(affected_files)
- return [output_api.PresubmitAddReviewers(suggested_reviewers)]
+ affected_files = set([f.LocalPath() for f in
+ input_api.change.AffectedFiles(source_file_filter)])
+ owners_db = input_api.owners_db
+ owner_email, approvers = _RietveldOwnerAndApprovers(input_api,
+ owners_db.email_regexp)
+ approvers_plus_owner = approvers.union(set([owner_email]))
+
+ missing_files = owners_db.files_not_covered_by(affected_files,
+ approvers_plus_owner)
+ if missing_files:
+ return [output_api.PresubmitError('Missing LGTM from an OWNER for: %s' %
+ ','.join(missing_files))]
+
+ if not approvers:
+ return [output_api.PresubmitError('Missing LGTM from someone other than %s'
+ % owner_email)]
+ return []
-def _Approvers(input_api, email_regexp):
- if not input_api.change.issue:
- return []
+def _RietveldOwnerAndApprovers(input_api, email_regexp):
+ """Return the owner and approvers of a change, if any."""
# TODO(dpranke): Should figure out if input_api.host_url is supposed to
# be a host or a scheme+host and normalize it there.
host = input_api.host_url
@@ -667,19 +669,56 @@ def _Approvers(input_api, email_regexp):
f = input_api.urllib2.urlopen(url)
issue_props = input_api.json.load(f)
- owner = input_api.re.escape(issue_props['owner'])
+ messages = issue_props.get('messages', [])
+ owner_email = issue_props['owner_email']
+ owner_regexp = input_api.re.compile(input_api.re.escape(owner_email))
+ approvers = _GetApprovers(messages, email_regexp, owner_regexp)
+
+ return (owner_email, set(approvers))
+
+
+def _IsApprovingComment(text):
+ """Implements the logic for parsing a change comment for approval."""
+
+ # Any comment that contains a non-quoted line containing an 'lgtm' is an
+ # approval.
+ #
+ # TODO(dpranke): this differs from the logic used inside Google in a few
+ # ways. Inside Google,
+ #
+ # 1) the approving phrase must appear at the beginning of the first non
+ # quoted-line in the comment.'
+ # 2) "LG", "Looks Good", and "Looks Good to Me" are also acceptable.
+ # 3) Subsequent comments from the reviewer can rescind approval, unless
+ # the phrase "LGTM++" was used.
+ # We should consider implementing some or all of this here.
+ for l in text.splitlines():
+ l = l.strip().lower()
+ if l.startswith('>'):
+ continue
+
+ if 'lgtm' in l:
+ return True
+ return False
+
+
+def _GetApprovers(messages, email_regexp, owner_regexp):
+ """Returns the set of approvers for a change given the owner and messages.
+
+ Messages should be a list of dicts containing 'sender' and 'text' keys."""
# TODO(dpranke): This mimics the logic in
# /tools/commit-queue/verifiers/reviewer_lgtm.py
# We should share the code and/or remove the check there where it is
# redundant (since the commit queue also enforces the presubmit checks).
def match_reviewer(r):
- return email_regexp.match(r) and not input_api.re.match(owner, r)
+ return email_regexp.match(r) and not owner_regexp.match(r)
approvers = []
- for m in issue_props.get('messages', []):
- if 'lgtm' in m['text'].lower() and match_reviewer(m['sender']):
- approvers.append(m['sender'])
+ for m in messages:
+ sender = m['sender']
+ if _IsApprovingComment(m['text']) and match_reviewer(sender):
+ approvers.append(sender)
return set(approvers)
@@ -776,10 +815,8 @@ def PanProjectChecks(input_api, output_api,
text_files = lambda x: input_api.FilterSourceFile(x, black_list=black_list,
white_list=white_list)
- # TODO(dpranke): enable upload as well
- if input_api.is_committing:
- results.extend(input_api.canned_checks.CheckOwners(
- input_api, output_api, source_file_filter=sources))
+ results.extend(input_api.canned_checks.CheckOwners(
+ input_api, output_api, source_file_filter=sources))
results.extend(input_api.canned_checks.CheckLongLines(
input_api, output_api, source_file_filter=sources))
« no previous file with comments | « no previous file | tests/presubmit_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698