| OLD | NEW |
| 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Generic presubmit checks that can be reused by other presubmit checks.""" | 5 """Generic presubmit checks that can be reused by other presubmit checks.""" |
| 6 | 6 |
| 7 | 7 |
| 8 ### Description checks | 8 ### Description checks |
| 9 | 9 |
| 10 def CheckChangeHasTestField(input_api, output_api): | 10 def CheckChangeHasTestField(input_api, output_api): |
| (...skipping 743 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 754 | 754 |
| 755 | 755 |
| 756 def _RietveldOwnerAndApprovers(input_api, email_regexp): | 756 def _RietveldOwnerAndApprovers(input_api, email_regexp): |
| 757 """Return the owner and approvers of a change, if any.""" | 757 """Return the owner and approvers of a change, if any.""" |
| 758 # TODO(dpranke): Should figure out if input_api.host_url is supposed to | 758 # TODO(dpranke): Should figure out if input_api.host_url is supposed to |
| 759 # be a host or a scheme+host and normalize it there. | 759 # be a host or a scheme+host and normalize it there. |
| 760 host = input_api.host_url | 760 host = input_api.host_url |
| 761 if not host.startswith('http://') and not host.startswith('https://'): | 761 if not host.startswith('http://') and not host.startswith('https://'): |
| 762 host = 'http://' + host | 762 host = 'http://' + host |
| 763 url = '%s/api/%s?messages=true' % (host, input_api.change.issue) | 763 url = '%s/api/%s?messages=true' % (host, input_api.change.issue) |
| 764 issue_props = input_api.json.load(input_api.urllib2.urlopen(url)) |
| 765 owner_email = issue_props['owner_email'] |
| 764 | 766 |
| 765 f = input_api.urllib2.urlopen(url) | 767 def match_reviewer(r): |
| 766 issue_props = input_api.json.load(f) | 768 return email_regexp.match(r) and r != owner_email |
| 769 |
| 767 messages = issue_props.get('messages', []) | 770 messages = issue_props.get('messages', []) |
| 768 owner_email = issue_props['owner_email'] | 771 approvers = set( |
| 769 owner_regexp = input_api.re.compile(input_api.re.escape(owner_email)) | 772 m['sender'] for m in messages |
| 770 approvers = _GetApprovers(messages, email_regexp, owner_regexp) | 773 if m.get('approval') and match_reviewer(m['sender'])) |
| 771 | 774 |
| 772 return (owner_email, set(approvers)) | 775 return owner_email, approvers |
| 773 | |
| 774 | |
| 775 def _IsApprovingComment(text): | |
| 776 """Implements the logic for parsing a change comment for approval.""" | |
| 777 | |
| 778 # Any comment that contains a non-quoted line containing an 'lgtm' is an | |
| 779 # approval. | |
| 780 # | |
| 781 # TODO(dpranke): this differs from the logic used inside Google in a few | |
| 782 # ways. Inside Google, | |
| 783 # | |
| 784 # 1) the approving phrase must appear at the beginning of the first non | |
| 785 # quoted-line in the comment.' | |
| 786 # 2) "LG", "Looks Good", and "Looks Good to Me" are also acceptable. | |
| 787 # 3) Subsequent comments from the reviewer can rescind approval, unless | |
| 788 # the phrase "LGTM++" was used. | |
| 789 # We should consider implementing some or all of this here. | |
| 790 for l in text.splitlines(): | |
| 791 l = l.strip().lower() | |
| 792 if l.startswith('>'): | |
| 793 continue | |
| 794 | |
| 795 if 'lgtm' in l: | |
| 796 return True | |
| 797 return False | |
| 798 | |
| 799 | |
| 800 def _GetApprovers(messages, email_regexp, owner_regexp): | |
| 801 """Returns the set of approvers for a change given the owner and messages. | |
| 802 | |
| 803 Messages should be a list of dicts containing 'sender' and 'text' keys.""" | |
| 804 | |
| 805 # TODO(dpranke): This mimics the logic in | |
| 806 # /tools/commit-queue/verifiers/reviewer_lgtm.py | |
| 807 # We should share the code and/or remove the check there where it is | |
| 808 # redundant (since the commit queue also enforces the presubmit checks). | |
| 809 def match_reviewer(r): | |
| 810 return email_regexp.match(r) and not owner_regexp.match(r) | |
| 811 | |
| 812 approvers = [] | |
| 813 for m in messages: | |
| 814 sender = m['sender'] | |
| 815 if _IsApprovingComment(m['text']) and match_reviewer(sender): | |
| 816 approvers.append(sender) | |
| 817 return set(approvers) | |
| 818 | 776 |
| 819 | 777 |
| 820 def _CheckConstNSObject(input_api, output_api, source_file_filter): | 778 def _CheckConstNSObject(input_api, output_api, source_file_filter): |
| 821 """Checks to make sure no objective-c files have |const NSSomeClass*|.""" | 779 """Checks to make sure no objective-c files have |const NSSomeClass*|.""" |
| 822 pattern = input_api.re.compile( | 780 pattern = input_api.re.compile( |
| 823 r'const\s+NS(?!(Point|Range|Rect|Size)\s*\*)\w*\s*\*') | 781 r'const\s+NS(?!(Point|Range|Rect|Size)\s*\*)\w*\s*\*') |
| 824 | 782 |
| 825 def objective_c_filter(f): | 783 def objective_c_filter(f): |
| 826 return (source_file_filter(f) and | 784 return (source_file_filter(f) and |
| 827 input_api.os_path.splitext(f.LocalPath())[1] in ('.h', '.m', '.mm')) | 785 input_api.os_path.splitext(f.LocalPath())[1] in ('.h', '.m', '.mm')) |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 950 results.extend(input_api.canned_checks.CheckChangeSvnEolStyle( | 908 results.extend(input_api.canned_checks.CheckChangeSvnEolStyle( |
| 951 input_api, output_api, source_file_filter=text_files)) | 909 input_api, output_api, source_file_filter=text_files)) |
| 952 snapshot("checking svn mime types") | 910 snapshot("checking svn mime types") |
| 953 results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes( | 911 results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes( |
| 954 input_api, output_api)) | 912 input_api, output_api)) |
| 955 snapshot("checking license") | 913 snapshot("checking license") |
| 956 results.extend(input_api.canned_checks.CheckLicense( | 914 results.extend(input_api.canned_checks.CheckLicense( |
| 957 input_api, output_api, license_header, source_file_filter=sources)) | 915 input_api, output_api, license_header, source_file_filter=sources)) |
| 958 snapshot("done") | 916 snapshot("done") |
| 959 return results | 917 return results |
| OLD | NEW |