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

Side by Side Diff: trychange.py

Issue 504085: Add --sup_rep support to trychange.py. (Closed)
Patch Set: Created 11 years 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 unified diff | Download patch
« tests/gcl_unittest.py ('K') | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2009 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2009 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 """Client-side script to send a try job to the try server. It communicates to 5 """Client-side script to send a try job to the try server. It communicates to
6 the try server by either writting to a svn repository or by directly connecting 6 the try server by either writting to a svn repository or by directly connecting
7 to the server by HTTP. 7 to the server by HTTP.
8 """ 8 """
9 9
10 import datetime 10 import datetime
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 def __str__(self): 65 def __str__(self):
66 return self.args[0] + '\n' + HELP_STRING 66 return self.args[0] + '\n' + HELP_STRING
67 67
68 68
69 def EscapeDot(name): 69 def EscapeDot(name):
70 return name.replace('.', '-') 70 return name.replace('.', '-')
71 71
72 72
73 class SCM(object): 73 class SCM(object):
74 """Simplistic base class to implement one function: ProcessOptions.""" 74 """Simplistic base class to implement one function: ProcessOptions."""
75 def __init__(self, options): 75 def __init__(self, options, cwd):
76 self.checkout_root = cwd
76 self.options = options 77 self.options = options
78 self.files = self.options.files
79 self.options.files = None
77 80
78 def GetFileNames(self): 81 def GetFileNames(self):
79 """Return the list of files in the diff.""" 82 """Return the list of files in the diff."""
80 return self.options.files 83 return self.files
81 84
82 85
83 class SVN(SCM): 86 class SVN(SCM):
84 """Gathers the options and diff for a subversion checkout.""" 87 """Gathers the options and diff for a subversion checkout."""
85 def __init__(self, *args, **kwargs): 88 def __init__(self, *args, **kwargs):
86 SCM.__init__(self, *args, **kwargs) 89 SCM.__init__(self, *args, **kwargs)
87 self.checkout_root = scm.SVN.GetCheckoutRoot(os.getcwd()) 90 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root)
88 if not self.options.diff:
89 # Generate the diff from the scm.
90 self.options.diff = self._GenerateDiff()
91 if not self.options.email: 91 if not self.options.email:
92 # Assumes the svn credential is an email address. 92 # Assumes the svn credential is an email address.
93 self.options.email = scm.SVN.GetEmail(self.checkout_root) 93 self.options.email = scm.SVN.GetEmail(self.checkout_root)
94 94
95 def _GenerateDiff(self): 95 def GenerateDiff(self):
96 """Returns a string containing the diff for the given file list. 96 """Returns a string containing the diff for the given file list.
97 97
98 The files in the list should either be absolute paths or relative to the 98 The files in the list should either be absolute paths or relative to the
99 given root. 99 given root.
100 """ 100 """
101 if not self.options.files: 101 if not self.files:
102 previous_cwd = os.getcwd() 102 previous_cwd = os.getcwd()
103 os.chdir(self.checkout_root) 103 os.chdir(self.checkout_root)
104 excluded = ['!', '?', 'X', ' ', '~'] 104 excluded = ['!', '?', 'X', ' ', '~']
105 self.options.files = [ 105 self.files = [
106 f[1] for f in scm.SVN.CaptureStatus(self.checkout_root) 106 f[1] for f in scm.SVN.CaptureStatus(self.checkout_root)
107 if f[0][0] not in excluded 107 if f[0][0] not in excluded
108 ] 108 ]
109 os.chdir(previous_cwd) 109 os.chdir(previous_cwd)
110 return scm.SVN.GenerateDiff(self.options.files, full_move=True) 110 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True)
111 111
112 def GetLocalRoot(self): 112 def GetLocalRoot(self):
113 """Return the path of the repository root.""" 113 """Return the path of the repository root."""
114 return self.checkout_root 114 return self.checkout_root
115 115
116 def GetBots(self): 116 def GetBots(self):
117 try: 117 try:
118 # Try to search on the subversion repository for the file. 118 # Try to search on the subversion repository for the file.
119 import gcl 119 import gcl
120 return gcl.GetCachedFile('PRESUBMIT.py', use_root=True) 120 return gcl.GetCachedFile('PRESUBMIT.py', use_root=True)
121 except ImportError: 121 except ImportError:
122 try: 122 try:
123 return gclient_utils.FileRead(os.path.join(self.checkout_root, 123 return gclient_utils.FileRead(os.path.join(self.checkout_root,
124 'PRESUBMIT.py')) 124 'PRESUBMIT.py'))
125 except (IOError, OSError): 125 except (IOError, OSError):
126 return None 126 return None
127 127
128 128
129 class GIT(SCM): 129 class GIT(SCM):
130 """Gathers the options and diff for a git checkout.""" 130 """Gathers the options and diff for a git checkout."""
131 def __init__(self, *args, **kwargs): 131 def __init__(self, *args, **kwargs):
132 SCM.__init__(self, *args, **kwargs) 132 SCM.__init__(self, *args, **kwargs)
133 self.checkout_root = scm.GIT.GetCheckoutRoot(os.getcwd()) 133 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root)
134 if not self.options.diff:
135 self.options.diff = scm.GIT.GenerateDiff(self.checkout_root,
136 full_move=True)
137 if not self.options.name: 134 if not self.options.name:
138 self.options.name = scm.GIT.GetPatchName(self.checkout_root) 135 self.options.name = scm.GIT.GetPatchName(self.checkout_root)
139 if not self.options.email: 136 if not self.options.email:
140 self.options.email = scm.GIT.GetEmail(self.checkout_root) 137 self.options.email = scm.GIT.GetEmail(self.checkout_root)
141 138
142 def GetLocalRoot(self): 139 def GetLocalRoot(self):
143 """Return the path of the repository root.""" 140 """Return the path of the repository root."""
144 return self.checkout_root 141 return self.checkout_root
145 142
143 def GenerateDiff(self):
144 # For now, ignores self.files
145 return scm.GIT.GenerateDiff(self.checkout_root, full_move=True)
146
146 def GetBots(self): 147 def GetBots(self):
147 try: 148 try:
148 # A git checkout is always a full checkout. 149 # A git checkout is always a full checkout.
149 return gclient_utils.FileRead(os.path.join(self.checkout_root, 150 return gclient_utils.FileRead(os.path.join(self.checkout_root,
150 'PRESUBMIT.py')) 151 'PRESUBMIT.py'))
151 except (IOError, OSError): 152 except (IOError, OSError):
152 return None 153 return None
153 154
154 155
155 def _ParseSendChangeOptions(options): 156 def _ParseSendChangeOptions(options):
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 gclient_utils.CheckCall(["svn", "commit", full_path, '--file', 285 gclient_utils.CheckCall(["svn", "commit", full_path, '--file',
285 temp_file.name], print_error=False) 286 temp_file.name], print_error=False)
286 except gclient_utils.CheckCallError, e: 287 except gclient_utils.CheckCallError, e:
287 raise NoTryServerAccess(' '.join(e.command) + '\nOuput:\n' + 288 raise NoTryServerAccess(' '.join(e.command) + '\nOuput:\n' +
288 e.stdout) 289 e.stdout)
289 finally: 290 finally:
290 temp_file.close() 291 temp_file.close()
291 shutil.rmtree(temp_dir, True) 292 shutil.rmtree(temp_dir, True)
292 293
293 294
294 def GuessVCS(options): 295 def GuessVCS(options, cwd):
295 """Helper to guess the version control system. 296 """Helper to guess the version control system.
296 297
297 NOTE: Very similar to upload.GuessVCS. Doesn't look for hg since we don't 298 NOTE: Very similar to upload.GuessVCS. Doesn't look for hg since we don't
298 support it yet. 299 support it yet.
299 300
300 This examines the current directory, guesses which SCM we're using, and 301 This examines the current directory, guesses which SCM we're using, and
301 returns an instance of the appropriate class. Exit with an error if we can't 302 returns an instance of the appropriate class. Exit with an error if we can't
302 figure it out. 303 figure it out.
303 304
304 Returns: 305 Returns:
305 A SCM instance. Exits if the SCM can't be guessed. 306 A SCM instance. Exits if the SCM can't be guessed.
306 """ 307 """
307 __pychecker__ = 'no-returnvalues' 308 __pychecker__ = 'no-returnvalues'
308 # Subversion has a .svn in all working directories. 309 # Subversion has a .svn in all working directories.
309 if os.path.isdir('.svn'): 310 if os.path.isdir(os.path.join(cwd, '.svn')):
310 logging.info("Guessed VCS = Subversion") 311 logging.info("Guessed VCS = Subversion")
311 return SVN(options) 312 return SVN(options, cwd)
312 313
313 # Git has a command to test if you're in a git tree. 314 # Git has a command to test if you're in a git tree.
314 # Try running it, but don't die if we don't have git installed. 315 # Try running it, but don't die if we don't have git installed.
315 try: 316 try:
316 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"]) 317 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"], cwd)
317 logging.info("Guessed VCS = Git") 318 logging.info("Guessed VCS = Git")
318 return GIT(options) 319 return GIT(options, cwd)
319 except gclient_utils.CheckCallError, e: 320 except gclient_utils.CheckCallError, e:
320 if e.retcode != 2: # ENOENT -- they don't have git installed. 321 if e.retcode != 2: # ENOENT -- they don't have git installed.
321 raise 322 raise
322 raise NoTryServerAccess("Could not guess version control system. " 323 raise NoTryServerAccess("Could not guess version control system. "
323 "Are you in a working copy directory?") 324 "Are you in a working copy directory?")
324 325
325 326
326 def TryChange(argv, 327 def TryChange(argv,
327 file_list, 328 file_list,
328 swallow_exception, 329 swallow_exception,
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
391 "try, relative to the repository root") 392 "try, relative to the repository root")
392 group.add_option("--diff", 393 group.add_option("--diff",
393 help="File containing the diff to try") 394 help="File containing the diff to try")
394 group.add_option("--url", 395 group.add_option("--url",
395 help="Url where to grab a patch") 396 help="Url where to grab a patch")
396 group.add_option("--root", 397 group.add_option("--root",
397 help="Root to use for the patch; base subdirectory for " 398 help="Root to use for the patch; base subdirectory for "
398 "patch created in a subdirectory") 399 "patch created in a subdirectory")
399 group.add_option("--patchlevel", type='int', metavar="LEVEL", 400 group.add_option("--patchlevel", type='int', metavar="LEVEL",
400 help="Used as -pN parameter to patch") 401 help="Used as -pN parameter to patch")
402 group.add_option("--sub_rep", action="append", default=["."],
403 help="Subcheckout to use in addition. This is mainly "
404 "useful for gclient-style checkouts.")
401 parser.add_option_group(group) 405 parser.add_option_group(group)
402 406
403 group = optparse.OptionGroup(parser, "Access the try server by HTTP") 407 group = optparse.OptionGroup(parser, "Access the try server by HTTP")
404 group.add_option("--use_http", 408 group.add_option("--use_http",
405 action="store_const", 409 action="store_const",
406 const=_SendChangeHTTP, 410 const=_SendChangeHTTP,
407 dest="send_patch", 411 dest="send_patch",
408 help="Use HTTP to talk to the try server [default]") 412 help="Use HTTP to talk to the try server [default]")
409 group.add_option("--host", 413 group.add_option("--host",
410 help="Host address") 414 help="Host address")
(...skipping 22 matching lines...) Expand all
433 # Switch the default accordingly if there was no default send_patch. 437 # Switch the default accordingly if there was no default send_patch.
434 if not options.send_patch: 438 if not options.send_patch:
435 if options.port and options.host: 439 if options.port and options.host:
436 options.send_patch = _SendChangeHTTP 440 options.send_patch = _SendChangeHTTP
437 elif options.svn_repo: 441 elif options.svn_repo:
438 options.send_patch = _SendChangeSVN 442 options.send_patch = _SendChangeSVN
439 else: 443 else:
440 parser.error('Please specify an access method.') 444 parser.error('Please specify an access method.')
441 445
442 try: 446 try:
447 # Process the VCS in any case at least to retrieve the email address.
448 checkouts = []
449 for item in options.sub_rep:
450 checkout = GuessVCS(options, item)
451 if checkout.GetLocalRoot() in [c.GetLocalRoot() for c in checkouts]:
452 parser.error('Specified the root %s two times.' %
453 checkout.GetLocalRoot())
454 checkouts.append(checkout)
455
443 # Convert options.diff into the content of the diff. 456 # Convert options.diff into the content of the diff.
444 if options.url: 457 if options.url:
445 if options.files: 458 if options.files:
446 parser.error('You cannot specify files and --url at the same time.') 459 parser.error('You cannot specify files and --url at the same time.')
447 options.diff = urllib.urlopen(options.url).read() 460 options.diff = urllib.urlopen(options.url).read()
448 elif options.diff: 461 elif options.diff:
449 if options.files: 462 if options.files:
450 parser.error('You cannot specify files and --diff at the same time.') 463 parser.error('You cannot specify files and --diff at the same time.')
451 options.diff = gclient_utils.FileRead(options.diff, 'rb') 464 options.diff = gclient_utils.FileRead(options.diff, 'rb')
452 # Process the VCS in any case at least to retrieve the email address. 465 else:
453 try: 466 # Use this as the base.
454 options.scm = GuessVCS(options) 467 root = checkouts[0].GetLocalRoot()
455 except NoTryServerAccess, e: 468 diffs = []
456 # If we got the diff, we don't care. 469 for checkout in checkouts:
457 if not options.diff: 470 diff = checkout.GenerateDiff().splitlines(True)
458 # TODO(maruel): Raise what? 471 # Munge it.
459 raise 472 path_diff = gclient_utils.PathDifference(root, checkout.GetLocalRoot())
473 for i in range(len(diff)):
474 if diff[i].startswith('--- ') or diff[i].startswith('+++ '):
475 diff[i] = diff[i][0:3] + path_diff + diff[i][4:]
476 diffs.extend(diff)
477 options.diff = ''.join(diffs)
460 478
461 # Get try slaves from PRESUBMIT.py files if not specified.
462 if not options.bot: 479 if not options.bot:
480 # Get try slaves from PRESUBMIT.py files if not specified.
463 # Even if the diff comes from options.url, use the local checkout for bot 481 # Even if the diff comes from options.url, use the local checkout for bot
464 # selection. 482 # selection.
465 try: 483 try:
466 # Get try slaves from PRESUBMIT.py files if not specified.
467 import presubmit_support 484 import presubmit_support
468 root_presubmit = options.scm.GetBots() 485 root_presubmit = checkouts[0].GetBots()
469 options.bot = presubmit_support.DoGetTrySlaves( 486 options.bot = presubmit_support.DoGetTrySlaves(
470 options.scm.GetFileNames(), 487 checkouts[0].GetFileNames(),
471 options.scm.GetLocalRoot(), 488 checkouts[0].GetLocalRoot(),
472 root_presubmit, 489 root_presubmit,
473 False, 490 False,
474 sys.stdout) 491 sys.stdout)
475 except ImportError: 492 except ImportError:
476 pass 493 pass
477 # If no bot is specified, either the default pool will be selected or the 494 # If no bot is specified, either the default pool will be selected or the
478 # try server will refuse the job. Either case we don't need to interfere. 495 # try server will refuse the job. Either case we don't need to interfere.
479 496
480 if options.name is None: 497 if options.name is None:
481 if options.issue: 498 if options.issue:
(...skipping 17 matching lines...) Expand all
499 except (InvalidScript, NoTryServerAccess), e: 516 except (InvalidScript, NoTryServerAccess), e:
500 if swallow_exception: 517 if swallow_exception:
501 return 1 518 return 1
502 print e 519 print e
503 return 1 520 return 1
504 return 0 521 return 0
505 522
506 523
507 if __name__ == "__main__": 524 if __name__ == "__main__":
508 sys.exit(TryChange(None, [], False)) 525 sys.exit(TryChange(None, [], False))
OLDNEW
« tests/gcl_unittest.py ('K') | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698