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 |