OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 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 """Client-side script to send a try job to the try server. It communicates to | 6 """Client-side script to send a try job to the try server. It communicates to |
7 the try server by either writting to a svn repository or by directly connecting | 7 the try server by either writting to a svn repository or by directly connecting |
8 to the server by HTTP. | 8 to the server by HTTP. |
9 """ | 9 """ |
10 | 10 |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
93 | 93 |
94 class SCM(object): | 94 class SCM(object): |
95 """Simplistic base class to implement one function: ProcessOptions.""" | 95 """Simplistic base class to implement one function: ProcessOptions.""" |
96 def __init__(self, options, path): | 96 def __init__(self, options, path): |
97 items = path.split('@') | 97 items = path.split('@') |
98 assert len(items) <= 2 | 98 assert len(items) <= 2 |
99 self.checkout_root = items[0] | 99 self.checkout_root = items[0] |
100 items.append(None) | 100 items.append(None) |
101 self.diff_against = items[1] | 101 self.diff_against = items[1] |
102 self.options = options | 102 self.options = options |
103 self.files = self.options.files | 103 # Lazy-load file list from the SCM unless files were specified in options. |
104 self._files = None | |
105 self._file_tuples = None | |
106 if self.options.files != []: | |
M-A Ruel
2011/09/27 14:13:00
if self.options.files:
?
Alexei Svitkine (slow)
2011/09/27 14:21:01
Done.
| |
107 self._files = self.options.files | |
108 self._file_tuples = [('M', f) for f in self.files] | |
104 self.options.files = None | 109 self.options.files = None |
105 self.codereview_settings = None | 110 self.codereview_settings = None |
106 self.codereview_settings_file = 'codereview.settings' | 111 self.codereview_settings_file = 'codereview.settings' |
107 self.gclient_root = None | 112 self.gclient_root = None |
108 | 113 |
109 def GetFileNames(self): | 114 def GetFileNames(self): |
110 """Return the list of files in the diff.""" | 115 """Return the list of files in the diff.""" |
111 return self.files | 116 return self.files |
112 | 117 |
113 def GetCodeReviewSetting(self, key): | 118 def GetCodeReviewSetting(self, key): |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
180 assert cur.startswith(root), (root, cur) | 185 assert cur.startswith(root), (root, cur) |
181 while cur.startswith(root): | 186 while cur.startswith(root): |
182 filepath = os.path.join(cur, filename) | 187 filepath = os.path.join(cur, filename) |
183 if os.path.isfile(filepath): | 188 if os.path.isfile(filepath): |
184 logging.info('Found %s at %s' % (filename, cur)) | 189 logging.info('Found %s at %s' % (filename, cur)) |
185 return gclient_utils.FileRead(filepath) | 190 return gclient_utils.FileRead(filepath) |
186 cur = os.path.dirname(cur) | 191 cur = os.path.dirname(cur) |
187 logging.warning('Didn\'t find %s' % filename) | 192 logging.warning('Didn\'t find %s' % filename) |
188 return None | 193 return None |
189 | 194 |
195 def _SetFileTuples(self, file_tuples): | |
196 excluded = ['!', '?', 'X', ' ', '~'] | |
197 def Excluded(f): | |
198 if f[0][0] in excluded: | |
199 return True | |
200 for r in self.options.exclude: | |
201 if re.search(r, f[1]): | |
202 logging.info('Ignoring "%s"' % f[1]) | |
203 return True | |
204 return False | |
205 | |
206 self._file_tuples = [f for f in file_tuples if not Excluded(f)] | |
207 self._files = [f[1] for f in self._file_tuples] | |
208 | |
209 def CaptureStatus(self): | |
210 """Returns the 'svn status' emulated output as an array of (status, file) | |
211 tuples.""" | |
212 raise NotImplementedError( | |
213 "abstract method -- subclass %s must override" % self.__class__) | |
214 | |
215 @property | |
216 def files(self): | |
217 if self._files is None: | |
218 self._SetFileTuples(self.CaptureStatus()) | |
219 return self._files | |
220 | |
221 @property | |
222 def file_tuples(self): | |
223 if self._file_tuples is None: | |
224 self._SetFileTuples(self.CaptureStatus()) | |
225 return self._file_tuples | |
226 | |
190 | 227 |
191 class SVN(SCM): | 228 class SVN(SCM): |
192 """Gathers the options and diff for a subversion checkout.""" | 229 """Gathers the options and diff for a subversion checkout.""" |
193 def __init__(self, *args, **kwargs): | 230 def __init__(self, *args, **kwargs): |
194 SCM.__init__(self, *args, **kwargs) | 231 SCM.__init__(self, *args, **kwargs) |
195 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root) | 232 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root) |
196 if not self.options.email: | 233 if not self.options.email: |
197 # Assumes the svn credential is an email address. | 234 # Assumes the svn credential is an email address. |
198 self.options.email = scm.SVN.GetEmail(self.checkout_root) | 235 self.options.email = scm.SVN.GetEmail(self.checkout_root) |
199 logging.info("SVN(%s)" % self.checkout_root) | 236 logging.info("SVN(%s)" % self.checkout_root) |
200 | 237 |
201 def ReadRootFile(self, filename): | 238 def ReadRootFile(self, filename): |
202 data = SCM.ReadRootFile(self, filename) | 239 data = SCM.ReadRootFile(self, filename) |
203 if data: | 240 if data: |
204 return data | 241 return data |
205 | 242 |
206 # Try to search on the subversion repository for the file. | 243 # Try to search on the subversion repository for the file. |
207 if not gcl: | 244 if not gcl: |
208 return None | 245 return None |
209 data = gcl.GetCachedFile(filename) | 246 data = gcl.GetCachedFile(filename) |
210 logging.debug('%s:\n%s' % (filename, data)) | 247 logging.debug('%s:\n%s' % (filename, data)) |
211 return data | 248 return data |
212 | 249 |
250 def CaptureStatus(self): | |
251 previous_cwd = os.getcwd() | |
252 os.chdir(self.checkout_root) | |
253 result = scm.SVN.CaptureStatus(self.checkout_root) | |
254 os.chdir(previous_cwd) | |
255 return result | |
256 | |
213 def GenerateDiff(self): | 257 def GenerateDiff(self): |
214 """Returns a string containing the diff for the given file list. | 258 """Returns a string containing the diff for the given file list. |
215 | 259 |
216 The files in the list should either be absolute paths or relative to the | 260 The files in the list should either be absolute paths or relative to the |
217 given root. | 261 given root. |
218 """ | 262 """ |
219 if not self.files: | |
220 previous_cwd = os.getcwd() | |
221 os.chdir(self.checkout_root) | |
222 | |
223 excluded = ['!', '?', 'X', ' ', '~'] | |
224 def Excluded(f): | |
225 if f[0][0] in excluded: | |
226 return True | |
227 for r in self.options.exclude: | |
228 if re.search(r, f[1]): | |
229 logging.info('Ignoring "%s"' % f[1]) | |
230 return True | |
231 return False | |
232 | |
233 self.files = [f[1] for f in scm.SVN.CaptureStatus(self.checkout_root) | |
234 if not Excluded(f)] | |
235 os.chdir(previous_cwd) | |
236 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True, | 263 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True, |
237 revision=self.diff_against) | 264 revision=self.diff_against) |
238 | 265 |
239 | 266 |
240 class GIT(SCM): | 267 class GIT(SCM): |
241 """Gathers the options and diff for a git checkout.""" | 268 """Gathers the options and diff for a git checkout.""" |
242 def __init__(self, *args, **kwargs): | 269 def __init__(self, *args, **kwargs): |
243 SCM.__init__(self, *args, **kwargs) | 270 SCM.__init__(self, *args, **kwargs) |
244 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root) | 271 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root) |
245 if not self.options.name: | 272 if not self.options.name: |
246 self.options.name = scm.GIT.GetPatchName(self.checkout_root) | 273 self.options.name = scm.GIT.GetPatchName(self.checkout_root) |
247 if not self.options.email: | 274 if not self.options.email: |
248 self.options.email = scm.GIT.GetEmail(self.checkout_root) | 275 self.options.email = scm.GIT.GetEmail(self.checkout_root) |
249 if not self.diff_against: | 276 if not self.diff_against: |
250 self.diff_against = scm.GIT.GetUpstreamBranch(self.checkout_root) | 277 self.diff_against = scm.GIT.GetUpstreamBranch(self.checkout_root) |
251 if not self.diff_against: | 278 if not self.diff_against: |
252 raise NoTryServerAccess( | 279 raise NoTryServerAccess( |
253 "Unable to determine default branch to diff against. " | 280 "Unable to determine default branch to diff against. " |
254 "Verify this branch is set up to track another" | 281 "Verify this branch is set up to track another" |
255 "(via the --track argument to \"git checkout -b ...\"") | 282 "(via the --track argument to \"git checkout -b ...\"") |
256 logging.info("GIT(%s)" % self.checkout_root) | 283 logging.info("GIT(%s)" % self.checkout_root) |
257 | 284 |
285 def CaptureStatus(self): | |
286 return scm.GIT.CaptureStatus(self.checkout_root, self.diff_against) | |
287 | |
258 def GenerateDiff(self): | 288 def GenerateDiff(self): |
259 if not self.files: | |
260 self.files = scm.GIT.GetDifferentFiles(self.checkout_root, | |
261 branch=self.diff_against) | |
262 | |
263 def NotExcluded(f): | |
264 for r in self.options.exclude: | |
265 if re.search(r, f): | |
266 logging.info('Ignoring "%s"' % f) | |
267 return False | |
268 return True | |
269 | |
270 self.files = filter(NotExcluded, self.files) | |
271 return scm.GIT.GenerateDiff(self.checkout_root, files=self.files, | 289 return scm.GIT.GenerateDiff(self.checkout_root, files=self.files, |
272 full_move=True, | 290 full_move=True, |
273 branch=self.diff_against) | 291 branch=self.diff_against) |
274 | 292 |
275 | 293 |
276 def _ParseSendChangeOptions(options): | 294 def _ParseSendChangeOptions(options): |
277 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" | 295 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" |
278 values = {} | 296 values = {} |
279 if options.email: | 297 if options.email: |
280 values['email'] = options.email | 298 values['email'] = options.email |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
457 def GetMungedDiff(path_diff, diff): | 475 def GetMungedDiff(path_diff, diff): |
458 # Munge paths to match svn. | 476 # Munge paths to match svn. |
459 for i in range(len(diff)): | 477 for i in range(len(diff)): |
460 if diff[i].startswith('--- ') or diff[i].startswith('+++ '): | 478 if diff[i].startswith('--- ') or diff[i].startswith('+++ '): |
461 new_file = posixpath.join(path_diff, diff[i][4:]).replace('\\', '/') | 479 new_file = posixpath.join(path_diff, diff[i][4:]).replace('\\', '/') |
462 diff[i] = diff[i][0:4] + new_file | 480 diff[i] = diff[i][0:4] + new_file |
463 return diff | 481 return diff |
464 | 482 |
465 | 483 |
466 def TryChange(argv, | 484 def TryChange(argv, |
485 change, | |
467 file_list, | 486 file_list, |
468 swallow_exception, | 487 swallow_exception, |
469 prog=None, | 488 prog=None, |
470 extra_epilog=None): | 489 extra_epilog=None): |
471 """ | 490 """ |
472 Args: | 491 Args: |
473 argv: Arguments and options. | 492 argv: Arguments and options. |
474 file_list: Default value to pass to --file. | 493 file_list: Default value to pass to --file. |
475 swallow_exception: Whether we raise or swallow exceptions. | 494 swallow_exception: Whether we raise or swallow exceptions. |
476 """ | 495 """ |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
694 # Use this as the base. | 713 # Use this as the base. |
695 root = checkouts[0].checkout_root | 714 root = checkouts[0].checkout_root |
696 diffs = [] | 715 diffs = [] |
697 for checkout in checkouts: | 716 for checkout in checkouts: |
698 diff = checkout.GenerateDiff().splitlines(True) | 717 diff = checkout.GenerateDiff().splitlines(True) |
699 path_diff = gclient_utils.PathDifference(root, checkout.checkout_root) | 718 path_diff = gclient_utils.PathDifference(root, checkout.checkout_root) |
700 # Munge it. | 719 # Munge it. |
701 diffs.extend(GetMungedDiff(path_diff, diff)) | 720 diffs.extend(GetMungedDiff(path_diff, diff)) |
702 options.diff = ''.join(diffs) | 721 options.diff = ''.join(diffs) |
703 | 722 |
723 if not options.name: | |
724 if options.issue: | |
725 options.name = 'Issue %s' % options.issue | |
726 else: | |
727 options.name = 'Unnamed' | |
728 print('Note: use --name NAME to change the try job name.') | |
729 | |
730 if not options.email: | |
731 parser.error('Using an anonymous checkout. Please use --email or set ' | |
732 'the TRYBOT_RESULTS_EMAIL_ADDRESS environment variable.') | |
733 print('Results will be emailed to: ' + options.email) | |
734 | |
704 if not options.bot: | 735 if not options.bot: |
705 # Get try slaves from PRESUBMIT.py files if not specified. | 736 # Get try slaves from PRESUBMIT.py files if not specified. |
706 # Even if the diff comes from options.url, use the local checkout for bot | 737 # Even if the diff comes from options.url, use the local checkout for bot |
707 # selection. | 738 # selection. |
708 try: | 739 try: |
709 import presubmit_support | 740 import presubmit_support |
710 root_presubmit = checkouts[0].ReadRootFile('PRESUBMIT.py') | 741 root_presubmit = checkouts[0].ReadRootFile('PRESUBMIT.py') |
742 if change is None: | |
743 change = presubmit_support.Change(options.name, | |
744 '', | |
745 checkouts[0].checkout_root, | |
746 checkouts[0].file_tuples, | |
747 options.issue, | |
748 options.patchset, | |
749 options.email) | |
711 options.bot = presubmit_support.DoGetTrySlaves( | 750 options.bot = presubmit_support.DoGetTrySlaves( |
751 change, | |
712 checkouts[0].GetFileNames(), | 752 checkouts[0].GetFileNames(), |
713 checkouts[0].checkout_root, | 753 checkouts[0].checkout_root, |
714 root_presubmit, | 754 root_presubmit, |
715 options.project, | 755 options.project, |
716 False, | 756 False, |
717 sys.stdout) | 757 sys.stdout) |
718 except ImportError: | 758 except ImportError: |
719 pass | 759 pass |
720 # If no bot is specified, either the default pool will be selected or the | 760 # If no bot is specified, either the default pool will be selected or the |
721 # try server will refuse the job. Either case we don't need to interfere. | 761 # try server will refuse the job. Either case we don't need to interfere. |
722 | 762 |
723 if options.name is None: | |
724 if options.issue: | |
725 options.name = 'Issue %s' % options.issue | |
726 else: | |
727 options.name = 'Unnamed' | |
728 print('Note: use --name NAME to change the try job name.') | |
729 if not options.email: | |
730 parser.error('Using an anonymous checkout. Please use --email or set ' | |
731 'the TRYBOT_RESULTS_EMAIL_ADDRESS environment variable.') | |
732 else: | |
733 print('Results will be emailed to: ' + options.email) | |
734 | |
735 # Prevent rietveld updates if we aren't running all the tests. | 763 # Prevent rietveld updates if we aren't running all the tests. |
736 if options.testfilter is not None: | 764 if options.testfilter is not None: |
737 options.issue = None | 765 options.issue = None |
738 options.patchset = None | 766 options.patchset = None |
739 | 767 |
740 # Send the patch. | 768 # Send the patch. |
741 if options.send_patch: | 769 if options.send_patch: |
742 # If forced. | 770 # If forced. |
743 options.send_patch(options) | 771 options.send_patch(options) |
744 PrintSuccess(options) | 772 PrintSuccess(options) |
(...skipping 15 matching lines...) Expand all Loading... | |
760 print >> sys.stderr, e | 788 print >> sys.stderr, e |
761 return 1 | 789 return 1 |
762 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 790 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
763 print >> sys.stderr, e | 791 print >> sys.stderr, e |
764 return 1 | 792 return 1 |
765 return 0 | 793 return 0 |
766 | 794 |
767 | 795 |
768 if __name__ == "__main__": | 796 if __name__ == "__main__": |
769 fix_encoding.fix_encoding() | 797 fix_encoding.fix_encoding() |
770 sys.exit(TryChange(None, [], False)) | 798 sys.exit(TryChange(None, None, [], False)) |
OLD | NEW |