OLD | NEW |
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 |
11 import getpass | 11 import getpass |
12 import logging | 12 import logging |
13 import optparse | 13 import optparse |
14 import os | 14 import os |
15 import shutil | 15 import shutil |
16 import socket | 16 import socket |
17 import subprocess | 17 import subprocess |
18 import sys | 18 import sys |
19 import tempfile | 19 import tempfile |
20 import urllib | 20 import urllib |
21 | 21 |
22 import breakpad | 22 import breakpad |
23 | 23 |
24 import gcl | 24 import gcl |
| 25 import gclient_utils |
25 import scm | 26 import scm |
26 import presubmit_support | 27 import presubmit_support |
27 import upload | 28 import upload |
28 | 29 |
29 __version__ = '1.1.2' | 30 __version__ = '1.1.2' |
30 | 31 |
31 | 32 |
32 # Constants | 33 # Constants |
33 HELP_STRING = "Sorry, Tryserver is not available." | 34 HELP_STRING = "Sorry, Tryserver is not available." |
34 USAGE = r"""%prog [change_name] [options] | 35 USAGE = r"""%prog [change_name] [options] |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 settings['default_transport'] = 'http' | 112 settings['default_transport'] = 'http' |
112 elif settings.get('svn_repo'): | 113 elif settings.get('svn_repo'): |
113 settings['default_transport'] = 'svn' | 114 settings['default_transport'] = 'svn' |
114 return settings | 115 return settings |
115 | 116 |
116 | 117 |
117 def EscapeDot(name): | 118 def EscapeDot(name): |
118 return name.replace('.', '-') | 119 return name.replace('.', '-') |
119 | 120 |
120 | 121 |
121 def RunCommand(command): | |
122 output, retcode = gcl.RunShellWithReturnCode(command) | |
123 if retcode: | |
124 raise NoTryServerAccess(' '.join(command) + '\nOuput:\n' + output) | |
125 return output | |
126 | |
127 | |
128 class SCM(object): | 122 class SCM(object): |
129 """Simplistic base class to implement one function: ProcessOptions.""" | 123 """Simplistic base class to implement one function: ProcessOptions.""" |
130 def __init__(self, options): | 124 def __init__(self, options): |
131 self.options = options | 125 self.options = options |
132 | 126 |
133 def ProcessOptions(self): | 127 def ProcessOptions(self): |
134 raise NotImplementedError | 128 raise NotImplementedError |
135 | 129 |
136 | 130 |
137 class SVN(SCM): | 131 class SVN(SCM): |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
295 ' try server svn repository to connect to.') | 289 ' try server svn repository to connect to.') |
296 | 290 |
297 values = _ParseSendChangeOptions(options) | 291 values = _ParseSendChangeOptions(options) |
298 description = '' | 292 description = '' |
299 for (k,v) in values.iteritems(): | 293 for (k,v) in values.iteritems(): |
300 description += "%s=%s\n" % (k,v) | 294 description += "%s=%s\n" % (k,v) |
301 | 295 |
302 # Do an empty checkout. | 296 # Do an empty checkout. |
303 temp_dir = tempfile.mkdtemp() | 297 temp_dir = tempfile.mkdtemp() |
304 temp_file = tempfile.NamedTemporaryFile() | 298 temp_file = tempfile.NamedTemporaryFile() |
305 temp_file_name = temp_file.name | |
306 try: | 299 try: |
307 # Don't use '--non-interactive', since we want it to prompt for | 300 try: |
308 # crendentials if necessary. | 301 command = ['svn', 'checkout', '--depth', 'empty', '-q', |
309 command = ['svn', 'checkout', '--depth', 'empty', '-q', | 302 options.svn_repo, temp_dir] |
310 options.svn_repo, temp_dir] | 303 if options.email: |
311 if options.email: | 304 command += ['--username', options.email] |
312 command += ['--username', options.email] | 305 gclient_utils.CheckCall(command) |
313 # Don't use RunCommand() since svn may prompt for information. | |
314 use_shell = sys.platform.startswith("win") | |
315 subprocess.Popen(command, shell=use_shell).communicate() | |
316 | 306 |
317 # TODO(maruel): Use a subdirectory per user? | 307 # TODO(maruel): Use a subdirectory per user? |
318 current_time = str(datetime.datetime.now()).replace(':', '.') | 308 current_time = str(datetime.datetime.now()).replace(':', '.') |
319 file_name = (EscapeDot(options.user) + '.' + EscapeDot(options.name) + | 309 file_name = (EscapeDot(options.user) + '.' + EscapeDot(options.name) + |
320 '.%s.diff' % current_time) | 310 '.%s.diff' % current_time) |
321 full_path = os.path.join(temp_dir, file_name) | 311 full_path = os.path.join(temp_dir, file_name) |
322 full_url = options.svn_repo + '/' + file_name | 312 full_url = options.svn_repo + '/' + file_name |
323 file_found = False | 313 file_found = False |
324 try: | 314 try: |
325 RunCommand(['svn', 'ls', full_url]) | 315 gclient_utils.CheckCall(['svn', 'ls', full_url]) |
326 file_found = True | 316 file_found = True |
327 except NoTryServerAccess: | 317 except gclient_utils.CheckCallError: |
328 pass | 318 pass |
329 if file_found: | 319 if file_found: |
330 # The file already exists in the repo. Note that commiting a file is a | 320 # The file already exists in the repo. Note that commiting a file is a |
331 # no-op if the file's content (the diff) is not modified. This is why the | 321 # no-op if the file's content (the diff) is not modified. This is why |
332 # file name contains the date and time. | 322 # the file name contains the date and time. |
333 RunCommand(['svn', 'update', full_path]) | 323 gclient_utils.CheckCall(['svn', 'update', full_path]) |
334 f = open(full_path, 'wb') | 324 f = open(full_path, 'wb') |
335 f.write(options.diff) | 325 f.write(options.diff) |
336 f.close() | 326 f.close() |
337 else: | 327 else: |
338 # Add the file to the repo | 328 # Add the file to the repo |
339 f = open(full_path, 'wb') | 329 f = open(full_path, 'wb') |
340 f.write(options.diff) | 330 f.write(options.diff) |
341 f.close() | 331 f.close() |
342 RunCommand(["svn", "add", full_path]) | 332 gclient_utils.CheckCall(["svn", "add", full_path]) |
343 temp_file.write(description) | 333 temp_file.write(description) |
344 temp_file.flush() | 334 temp_file.flush() |
345 RunCommand(["svn", "commit", full_path, '--file', | 335 gclient_utils.CheckCall(["svn", "commit", full_path, '--file', |
346 temp_file_name]) | 336 temp_file.name]) |
| 337 except gclient_utils.CheckCallError, e: |
| 338 raise NoTryServerAccess(' '.join(e.command) + '\nOuput:\n' + |
| 339 e.stdout) |
347 finally: | 340 finally: |
348 temp_file.close() | 341 temp_file.close() |
349 shutil.rmtree(temp_dir, True) | 342 shutil.rmtree(temp_dir, True) |
350 | 343 |
351 | 344 |
352 def GuessVCS(options): | 345 def GuessVCS(options): |
353 """Helper to guess the version control system. | 346 """Helper to guess the version control system. |
354 | 347 |
355 NOTE: Very similar to upload.GuessVCS. Doesn't look for hg since we don't | 348 NOTE: Very similar to upload.GuessVCS. Doesn't look for hg since we don't |
356 support it yet. | 349 support it yet. |
357 | 350 |
358 This examines the current directory, guesses which SCM we're using, and | 351 This examines the current directory, guesses which SCM we're using, and |
359 returns an instance of the appropriate class. Exit with an error if we can't | 352 returns an instance of the appropriate class. Exit with an error if we can't |
360 figure it out. | 353 figure it out. |
361 | 354 |
362 Returns: | 355 Returns: |
363 A SCM instance. Exits if the SCM can't be guessed. | 356 A SCM instance. Exits if the SCM can't be guessed. |
364 """ | 357 """ |
365 __pychecker__ = 'no-returnvalues' | 358 __pychecker__ = 'no-returnvalues' |
366 # Subversion has a .svn in all working directories. | 359 # Subversion has a .svn in all working directories. |
367 if os.path.isdir('.svn'): | 360 if os.path.isdir('.svn'): |
368 logging.info("Guessed VCS = Subversion") | 361 logging.info("Guessed VCS = Subversion") |
369 return SVN(options) | 362 return SVN(options) |
370 | 363 |
371 # Git has a command to test if you're in a git tree. | 364 # Git has a command to test if you're in a git tree. |
372 # Try running it, but don't die if we don't have git installed. | 365 # Try running it, but don't die if we don't have git installed. |
373 try: | 366 try: |
374 out, returncode = gcl.RunShellWithReturnCode(["git", "rev-parse", | 367 out, returncode = subprocess.Popen( |
375 "--is-inside-work-tree"]) | 368 ["git", "rev-parse", "--is-inside-work-tree"], |
| 369 shell=sys.platform.startswith('win'), |
| 370 stdout=subprocess.PIPE).communicate()[0] |
376 if returncode == 0: | 371 if returncode == 0: |
377 logging.info("Guessed VCS = Git") | 372 logging.info("Guessed VCS = Git") |
378 return GIT(options) | 373 return GIT(options) |
379 except OSError, (errno, message): | 374 except OSError, (errno, message): |
380 if errno != 2: # ENOENT -- they don't have git installed. | 375 if errno != 2: # ENOENT -- they don't have git installed. |
381 raise | 376 raise |
382 | 377 |
383 raise NoTryServerAccess("Could not guess version control system. " | 378 raise NoTryServerAccess("Could not guess version control system. " |
384 "Are you in a working copy directory?") | 379 "Are you in a working copy directory?") |
385 | 380 |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
513 not options.diff and not options.url): | 508 not options.diff and not options.url): |
514 # TODO(maruel): It should just try the modified files showing up in a | 509 # TODO(maruel): It should just try the modified files showing up in a |
515 # svn status. | 510 # svn status. |
516 parser.error('Nothing to try, changelist is empty.') | 511 parser.error('Nothing to try, changelist is empty.') |
517 | 512 |
518 try: | 513 try: |
519 # Convert options.diff into the content of the diff. | 514 # Convert options.diff into the content of the diff. |
520 if options.url: | 515 if options.url: |
521 options.diff = urllib.urlopen(options.url).read() | 516 options.diff = urllib.urlopen(options.url).read() |
522 elif options.diff: | 517 elif options.diff: |
523 options.diff = gcl.gclient_utils.FileRead(options.diff, 'rb') | 518 options.diff = gclient_utils.FileRead(options.diff, 'rb') |
524 # Process the VCS in any case at least to retrieve the email address. | 519 # Process the VCS in any case at least to retrieve the email address. |
525 try: | 520 try: |
526 options.scm = GuessVCS(options) | 521 options.scm = GuessVCS(options) |
527 options.scm.ProcessOptions() | 522 options.scm.ProcessOptions() |
528 except NoTryServerAccess, e: | 523 except NoTryServerAccess, e: |
529 # If we got the diff, we don't care. | 524 # If we got the diff, we don't care. |
530 if not options.diff: | 525 if not options.diff: |
531 # TODO(maruel): Raise what? | 526 # TODO(maruel): Raise what? |
532 raise | 527 raise |
533 | 528 |
(...skipping 27 matching lines...) Expand all Loading... |
561 except (InvalidScript, NoTryServerAccess), e: | 556 except (InvalidScript, NoTryServerAccess), e: |
562 if swallow_exception: | 557 if swallow_exception: |
563 return 1 | 558 return 1 |
564 print e | 559 print e |
565 return 1 | 560 return 1 |
566 return 0 | 561 return 0 |
567 | 562 |
568 | 563 |
569 if __name__ == "__main__": | 564 if __name__ == "__main__": |
570 sys.exit(TryChange(None, None, False)) | 565 sys.exit(TryChange(None, None, False)) |
OLD | NEW |