Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Enables directory-specific presubmit checks to run at upload and/or commit. | 6 """Enables directory-specific presubmit checks to run at upload and/or commit. |
| 7 """ | 7 """ |
| 8 | 8 |
| 9 __version__ = '1.4' | 9 __version__ = '1.4' |
| 10 | 10 |
| (...skipping 794 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 805 | 805 |
| 806 | 806 |
| 807 class GitChange(Change): | 807 class GitChange(Change): |
| 808 _AFFECTED_FILES = GitAffectedFile | 808 _AFFECTED_FILES = GitAffectedFile |
| 809 | 809 |
| 810 def __init__(self, *args, **kwargs): | 810 def __init__(self, *args, **kwargs): |
| 811 Change.__init__(self, *args, **kwargs) | 811 Change.__init__(self, *args, **kwargs) |
| 812 self.scm = 'git' | 812 self.scm = 'git' |
| 813 | 813 |
| 814 | 814 |
| 815 class ChangeDescription(object): | |
|
M-A Ruel
2011/03/22 17:24:16
It's a slightly odd location for this class but it
| |
| 816 """Contains a parsed form of the change description.""" | |
| 817 MAX_SUBJECT_LENGTH = 100 | |
| 818 | |
| 819 def __init__(self, subject=None, description=None, reviewers=None, tbr=False, | |
| 820 editor=None): | |
| 821 self.subject = (subject or '').strip() | |
| 822 self.description = (description or '').strip() | |
| 823 self.reviewers = reviewers or [] | |
| 824 self.tbr = tbr | |
| 825 self.editor = editor or gclient_utils.UserEdit | |
| 826 | |
| 827 if self.description: | |
| 828 if not self.description.startswith(self.subject): | |
| 829 self.description = self.subject + '\n\n' + self.description | |
| 830 elif self.subject: | |
| 831 self.description = self.subject | |
| 832 self.Parse(self.EditableDescription()) | |
| 833 | |
| 834 def EditableDescription(self): | |
| 835 text = self.description.strip() | |
| 836 if text: | |
| 837 text += '\n' | |
| 838 | |
| 839 tbr_present = False | |
| 840 r_present = False | |
| 841 bug_present = False | |
| 842 test_present = False | |
| 843 for l in text.splitlines(): | |
| 844 l = l.strip() | |
| 845 r_present = r_present or l.startswith('R=') | |
| 846 tbr_present = tbr_present or l.startswith('TBR=') | |
| 847 | |
| 848 if text and not (r_present or tbr_present): | |
| 849 text += '\n' | |
| 850 | |
| 851 if not tbr_present and not r_present: | |
| 852 if self.tbr: | |
| 853 text += 'TBR=' + ','.join(self.reviewers) + '\n' | |
| 854 else: | |
| 855 text += 'R=' + ','.join(self.reviewers) + '\n' | |
| 856 if not bug_present: | |
| 857 text += 'BUG=\n' | |
| 858 if not test_present: | |
| 859 text += 'TEST=\n' | |
| 860 | |
| 861 return text | |
| 862 | |
| 863 def UserEdit(self): | |
| 864 """Allows the user to update the description. | |
| 865 | |
| 866 Uses the editor callback passed to the constructor.""" | |
| 867 self.Parse(self.editor(self.EditableDescription())) | |
| 868 | |
| 869 def Parse(self, text): | |
| 870 """Parse the text returned from UserEdit() and update our state.""" | |
| 871 parsed_lines = [] | |
| 872 reviewers_regexp = re.compile('\s*(TBR|R)=(.+)') | |
| 873 reviewers = [] | |
| 874 subject = '' | |
| 875 tbr = False | |
| 876 for l in text.splitlines(): | |
| 877 l = l.strip() | |
| 878 | |
| 879 # Throw away empty BUG=, TEST=, and R= lines. We leave in TBR= lines | |
| 880 # to indicate that this change was meant to be "unreviewed". | |
| 881 if l in ('BUG=', 'TEST=', 'R='): | |
| 882 continue | |
| 883 | |
| 884 if not subject: | |
| 885 subject = l | |
| 886 matched_reviewers = reviewers_regexp.match(l) | |
| 887 if matched_reviewers: | |
| 888 tbr = (matched_reviewers.group(1) == 'TBR') | |
| 889 reviewers.extend(matched_reviewers.group(2).split(',')) | |
| 890 parsed_lines.append(l) | |
| 891 | |
| 892 if len(subject) > self.MAX_SUBJECT_LENGTH: | |
| 893 subject = subject[:self.MAX_SUBJECT_LENGTH - 3] + '...' | |
| 894 | |
| 895 self.description = '\n'.join(parsed_lines).strip() | |
| 896 self.subject = subject | |
| 897 self.reviewers = reviewers | |
| 898 self.tbr = tbr | |
| 899 | |
| 900 def IsEmpty(self): | |
| 901 return not self.description | |
| 902 | |
| 903 | |
| 904 | |
| 815 def ListRelevantPresubmitFiles(files, root): | 905 def ListRelevantPresubmitFiles(files, root): |
| 816 """Finds all presubmit files that apply to a given set of source files. | 906 """Finds all presubmit files that apply to a given set of source files. |
| 817 | 907 |
| 818 If inherit-review-settings-ok is present right under root, looks for | 908 If inherit-review-settings-ok is present right under root, looks for |
| 819 PRESUBMIT.py in directories enclosing root. | 909 PRESUBMIT.py in directories enclosing root. |
| 820 | 910 |
| 821 Args: | 911 Args: |
| 822 files: An iterable container containing file paths. | 912 files: An iterable container containing file paths. |
| 823 root: Path where to stop searching. | 913 root: Path where to stop searching. |
| 824 | 914 |
| (...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1186 options.verbose, | 1276 options.verbose, |
| 1187 sys.stdout, | 1277 sys.stdout, |
| 1188 sys.stdin, | 1278 sys.stdin, |
| 1189 options.default_presubmit, | 1279 options.default_presubmit, |
| 1190 options.may_prompt) | 1280 options.may_prompt) |
| 1191 return not results.should_continue() | 1281 return not results.should_continue() |
| 1192 | 1282 |
| 1193 | 1283 |
| 1194 if __name__ == '__main__': | 1284 if __name__ == '__main__': |
| 1195 sys.exit(Main(None)) | 1285 sys.exit(Main(None)) |
| OLD | NEW |