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

Side by Side Diff: trychange.py

Issue 123020: Add automatic try server settings detection support. (Closed)
Patch Set: merged to trunk Created 11 years, 6 months 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
« no previous file with comments | « 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 10
11 import datetime 11 import datetime
12 import getpass 12 import getpass
13 import logging 13 import logging
14 import optparse 14 import optparse
15 import os 15 import os
16 import shutil 16 import shutil
17 import socket
17 import sys 18 import sys
18 import tempfile 19 import tempfile
19 import traceback 20 import traceback
20 import urllib 21 import urllib
21 22
22 import gcl 23 import gcl
23 import gclient 24 import gclient
24 import upload 25 import upload
25 26
26 __version__ = '1.1' 27 __version__ = '1.1'
27 28
28 29
29 # Constants 30 # Constants
30 HELP_STRING = "Sorry, Tryserver is not available." 31 HELP_STRING = "Sorry, Tryserver is not available."
31 SCRIPT_PATH = os.path.join('tools', 'tryserver', 'tryserver.py')
32 USAGE = r"""%prog [options] 32 USAGE = r"""%prog [options]
33 33
34 Client-side script to send a try job to the try server. It communicates to 34 Client-side script to send a try job to the try server. It communicates to
35 the try server by either writting to a svn repository or by directly connecting 35 the try server by either writting to a svn repository or by directly connecting
36 to the server by HTTP. 36 to the server by HTTP.
37 37
38 38
39 Examples: 39 Examples:
40 A git patch off a web site (git inserts a/ and b/) and fix the base dir: 40 A git patch off a web site (git inserts a/ and b/) and fix the base dir:
41 %prog --url http://url/to/patch.diff --patchlevel 1 --root src 41 %prog --url http://url/to/patch.diff --patchlevel 1 --root src
(...skipping 26 matching lines...) Expand all
68 return None 68 return None
69 # If the root does not have a trailing \ or /, we add it so the returned path 69 # If the root does not have a trailing \ or /, we add it so the returned path
70 # starts immediately after the seperator regardless of whether it is provided. 70 # starts immediately after the seperator regardless of whether it is provided.
71 if not root.endswith(os.sep): 71 if not root.endswith(os.sep):
72 root += os.sep 72 root += os.sep
73 return subpath[len(root):] 73 return subpath[len(root):]
74 74
75 75
76 def GetSourceRoot(): 76 def GetSourceRoot():
77 """Returns the absolute directory one level up from the repository root.""" 77 """Returns the absolute directory one level up from the repository root."""
78 # TODO(maruel): This is odd to assume that '..' is the source root.
78 return os.path.abspath(os.path.join(gcl.GetRepositoryRoot(), '..')) 79 return os.path.abspath(os.path.join(gcl.GetRepositoryRoot(), '..'))
79 80
80 81
81 def ExecuteTryServerScript(): 82 def GetTryServerSettings():
82 """Locates the tryserver script, executes it and returns its dictionary. 83 """Grab try server settings local to the repository."""
84 def _SafeResolve(host):
85 try:
86 return socket.getaddrinfo(host, None)
87 except socket.gaierror:
88 return None
83 89
84 The try server script contains the repository-specific try server commands.""" 90 settings = {}
85 script_locals = {} 91 settings['http_port'] = gcl.GetCodeReviewSetting('TRYSERVER_HTTP_PORT')
86 try: 92 settings['http_host'] = gcl.GetCodeReviewSetting('TRYSERVER_HTTP_HOST')
87 # gcl.GetRepositoryRoot() may throw an exception. 93 settings['svn_repo'] = gcl.GetCodeReviewSetting('TRYSERVER_SVN_URL')
88 script_path = os.path.join(gcl.GetRepositoryRoot(), SCRIPT_PATH) 94 # Use http is the http_host name resolve, fallback to svn otherwise.
89 except Exception: 95 if (settings['http_port'] and settings['http_host'] and
90 return script_locals 96 _SafeResolve(settings['http_host'])):
91 if os.path.exists(script_path): 97 settings['default_transport'] = 'http'
92 try: 98 elif settings.get('svn_repo'):
93 exec(gcl.ReadFile(script_path), script_locals) 99 settings['default_transport'] = 'svn'
94 except Exception, e: 100 return settings
95 # TODO(maruel): Need to specialize the exception trapper.
96 traceback.print_exc()
97 raise InvalidScript('%s is invalid.' % script_path)
98 return script_locals
99 101
100 102
101 def EscapeDot(name): 103 def EscapeDot(name):
102 return name.replace('.', '-') 104 return name.replace('.', '-')
103 105
104 106
105 def RunCommand(command): 107 def RunCommand(command):
106 output, retcode = gcl.RunShellWithReturnCode(command) 108 output, retcode = gcl.RunShellWithReturnCode(command)
107 if retcode: 109 if retcode:
108 raise NoTryServerAccess(' '.join(command) + '\nOuput:\n' + output) 110 raise NoTryServerAccess(' '.join(command) + '\nOuput:\n' + output)
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
249 values['patchlevel'] = options.patchlevel 251 values['patchlevel'] = options.patchlevel
250 if options.issue: 252 if options.issue:
251 values['issue'] = options.issue 253 values['issue'] = options.issue
252 if options.patchset: 254 if options.patchset:
253 values['patchset'] = options.patchset 255 values['patchset'] = options.patchset
254 return values 256 return values
255 257
256 258
257 def _SendChangeHTTP(options): 259 def _SendChangeHTTP(options):
258 """Send a change to the try server using the HTTP protocol.""" 260 """Send a change to the try server using the HTTP protocol."""
259 script_locals = ExecuteTryServerScript()
260
261 if not options.host: 261 if not options.host:
262 options.host = script_locals.get('try_server_http_host', None) 262 raise NoTryServerAccess('Please use the --host option to specify the try '
263 if not options.host: 263 'server host to connect to.')
264 raise NoTryServerAccess('Please use the --host option to specify the try '
265 'server host to connect to.')
266 if not options.port: 264 if not options.port:
267 options.port = script_locals.get('try_server_http_port', None) 265 raise NoTryServerAccess('Please use the --port option to specify the try '
268 if not options.port: 266 'server port to connect to.')
269 raise NoTryServerAccess('Please use the --port option to specify the try '
270 'server port to connect to.')
271 267
272 values = _ParseSendChangeOptions(options) 268 values = _ParseSendChangeOptions(options)
273 values['patch'] = options.diff 269 values['patch'] = options.diff
274 270
275 url = 'http://%s:%s/send_try_patch' % (options.host, options.port) 271 url = 'http://%s:%s/send_try_patch' % (options.host, options.port)
276 proxies = None 272 proxies = None
277 if options.proxy: 273 if options.proxy:
278 if options.proxy.lower() == 'none': 274 if options.proxy.lower() == 'none':
279 # Effectively disable HTTP_PROXY or Internet settings proxy setup. 275 # Effectively disable HTTP_PROXY or Internet settings proxy setup.
280 proxies = {} 276 proxies = {}
(...skipping 10 matching lines...) Expand all
291 if not connection: 287 if not connection:
292 raise NoTryServerAccess('%s is unaccessible.' % url) 288 raise NoTryServerAccess('%s is unaccessible.' % url)
293 if connection.read() != 'OK': 289 if connection.read() != 'OK':
294 raise NoTryServerAccess('%s is unaccessible.' % url) 290 raise NoTryServerAccess('%s is unaccessible.' % url)
295 return options.name 291 return options.name
296 292
297 293
298 def _SendChangeSVN(options): 294 def _SendChangeSVN(options):
299 """Send a change to the try server by committing a diff file on a subversion 295 """Send a change to the try server by committing a diff file on a subversion
300 server.""" 296 server."""
301 script_locals = ExecuteTryServerScript()
302 if not options.svn_repo: 297 if not options.svn_repo:
303 options.svn_repo = script_locals.get('try_server_svn', None) 298 raise NoTryServerAccess('Please use the --svn_repo option to specify the'
304 if not options.svn_repo: 299 ' try server svn repository to connect to.')
305 raise NoTryServerAccess('Please use the --svn_repo option to specify the'
306 ' try server svn repository to connect to.')
307 300
308 values = _ParseSendChangeOptions(options) 301 values = _ParseSendChangeOptions(options)
309 description = '' 302 description = ''
310 for (k,v) in values.iteritems(): 303 for (k,v) in values.iteritems():
311 description += "%s=%s\n" % (k,v) 304 description += "%s=%s\n" % (k,v)
312 305
313 # Do an empty checkout. 306 # Do an empty checkout.
314 temp_dir = tempfile.mkdtemp() 307 temp_dir = tempfile.mkdtemp()
315 temp_file = tempfile.NamedTemporaryFile() 308 temp_file = tempfile.NamedTemporaryFile()
316 temp_file_name = temp_file.name 309 temp_file_name = temp_file.name
317 try: 310 try:
318 RunCommand(['svn', 'checkout', '--depth', 'empty', '--non-interactive', 311 # Don't use '--non-interactive', since we want it to prompt for
312 # crendentials if necessary.
313 RunCommand(['svn', 'checkout', '--depth', 'empty',
319 options.svn_repo, temp_dir]) 314 options.svn_repo, temp_dir])
320 # TODO(maruel): Use a subdirectory per user? 315 # TODO(maruel): Use a subdirectory per user?
321 current_time = str(datetime.datetime.now()).replace(':', '.') 316 current_time = str(datetime.datetime.now()).replace(':', '.')
322 file_name = (EscapeDot(options.user) + '.' + EscapeDot(options.name) + 317 file_name = (EscapeDot(options.user) + '.' + EscapeDot(options.name) +
323 '.%s.diff' % current_time) 318 '.%s.diff' % current_time)
324 full_path = os.path.join(temp_dir, file_name) 319 full_path = os.path.join(temp_dir, file_name)
325 full_url = options.svn_repo + '/' + file_name 320 full_url = options.svn_repo + '/' + file_name
326 file_found = False 321 file_found = False
327 try: 322 try:
328 RunCommand(['svn', 'ls', '--non-interactive', full_url]) 323 RunCommand(['svn', 'ls', '--non-interactive', full_url])
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
384 raise 379 raise
385 380
386 raise NoTryServerAccess("Could not guess version control system. " 381 raise NoTryServerAccess("Could not guess version control system. "
387 "Are you in a working copy directory?") 382 "Are you in a working copy directory?")
388 383
389 384
390 def TryChange(argv, 385 def TryChange(argv,
391 file_list, 386 file_list,
392 swallow_exception, 387 swallow_exception,
393 prog=None): 388 prog=None):
389 default_settings = GetTryServerSettings()
390 transport_functions = { 'http': _SendChangeHTTP, 'svn': _SendChangeSVN }
391 default_transport = transport_functions.get(
392 default_settings.get('default_transport'))
393
394 # Parse argv 394 # Parse argv
395 parser = optparse.OptionParser(usage=USAGE, 395 parser = optparse.OptionParser(usage=USAGE,
396 version=__version__, 396 version=__version__,
397 prog=prog) 397 prog=prog)
398 398
399 group = optparse.OptionGroup(parser, "Result and status") 399 group = optparse.OptionGroup(parser, "Result and status")
400 group.add_option("-u", "--user", default=getpass.getuser(), 400 group.add_option("-u", "--user", default=getpass.getuser(),
401 help="Owner user name [default: %default]") 401 help="Owner user name [default: %default]")
402 group.add_option("-e", "--email", default=os.environ.get('EMAIL_ADDRESS'), 402 group.add_option("-e", "--email", default=os.environ.get('EMAIL_ADDRESS'),
403 help="Email address where to send the results. Use the " 403 help="Email address where to send the results. Use the "
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
439 group.add_option("--url", 439 group.add_option("--url",
440 help="Url where to grab a patch") 440 help="Url where to grab a patch")
441 group.add_option("--root", 441 group.add_option("--root",
442 help="Root to use for the patch; base subdirectory for " 442 help="Root to use for the patch; base subdirectory for "
443 "patch created in a subdirectory") 443 "patch created in a subdirectory")
444 group.add_option("--patchlevel", type='int', metavar="LEVEL", 444 group.add_option("--patchlevel", type='int', metavar="LEVEL",
445 help="Used as -pN parameter to patch") 445 help="Used as -pN parameter to patch")
446 parser.add_option_group(group) 446 parser.add_option_group(group)
447 447
448 group = optparse.OptionGroup(parser, "Access the try server by HTTP") 448 group = optparse.OptionGroup(parser, "Access the try server by HTTP")
449 group.add_option("--use_http", action="store_const", const=_SendChangeHTTP, 449 group.add_option("--use_http",
450 dest="send_patch", default=_SendChangeHTTP, 450 action="store_const",
451 const=_SendChangeHTTP,
452 dest="send_patch",
453 default=default_transport,
451 help="Use HTTP to talk to the try server [default]") 454 help="Use HTTP to talk to the try server [default]")
452 group.add_option("--host", 455 group.add_option("--host",
456 default=default_settings['http_host'],
453 help="Host address") 457 help="Host address")
454 group.add_option("--port", 458 group.add_option("--port",
459 default=default_settings['http_port'],
455 help="HTTP port") 460 help="HTTP port")
456 group.add_option("--proxy", 461 group.add_option("--proxy",
457 help="HTTP proxy") 462 help="HTTP proxy")
458 parser.add_option_group(group) 463 parser.add_option_group(group)
459 464
460 group = optparse.OptionGroup(parser, "Access the try server with SVN") 465 group = optparse.OptionGroup(parser, "Access the try server with SVN")
461 group.add_option("--use_svn", action="store_const", const=_SendChangeSVN, 466 group.add_option("--use_svn",
467 action="store_const",
468 const=_SendChangeSVN,
462 dest="send_patch", 469 dest="send_patch",
463 help="Use SVN to talk to the try server") 470 help="Use SVN to talk to the try server")
464 group.add_option("--svn_repo", metavar="SVN_URL", 471 group.add_option("--svn_repo",
472 metavar="SVN_URL",
473 default=default_settings['svn_repo'],
465 help="SVN url to use to write the changes in; --use_svn is " 474 help="SVN url to use to write the changes in; --use_svn is "
466 "implied when using --svn_repo") 475 "implied when using --svn_repo")
467 parser.add_option_group(group) 476 parser.add_option_group(group)
468 477
469 options, args = parser.parse_args(argv) 478 options, args = parser.parse_args(argv)
470 # Switch the default accordingly. 479
471 if options.svn_repo: 480 # Switch the default accordingly if there was no default send_patch.
472 options.send_patch = _SendChangeSVN 481 if not options.send_patch:
482 if options.http_port and options.http_host:
483 options.send_patch = _SendChangeHTTP
484 elif options.svn_repo:
485 options.send_patch = _SendChangeSVN
473 486
474 if len(args) == 1 and args[0] == 'help': 487 if len(args) == 1 and args[0] == 'help':
475 parser.print_help() 488 parser.print_help()
476 if (not options.files and (not options.issue and options.patchset) and 489 if (not options.files and (not options.issue and options.patchset) and
477 not options.diff and not options.url): 490 not options.diff and not options.url):
478 # TODO(maruel): It should just try the modified files showing up in a 491 # TODO(maruel): It should just try the modified files showing up in a
479 # svn status. 492 # svn status.
480 print "Nothing to try, changelist is empty." 493 print "Nothing to try, changelist is empty."
481 return 494 return
482 495
(...skipping 18 matching lines...) Expand all
501 if patch_name == 'Unnamed': 514 if patch_name == 'Unnamed':
502 print "Note: use --name NAME to change the try's name." 515 print "Note: use --name NAME to change the try's name."
503 except (InvalidScript, NoTryServerAccess), e: 516 except (InvalidScript, NoTryServerAccess), e:
504 if swallow_exception: 517 if swallow_exception:
505 return 518 return
506 print e 519 print e
507 520
508 521
509 if __name__ == "__main__": 522 if __name__ == "__main__":
510 TryChange(None, None, False) 523 TryChange(None, None, False)
OLDNEW
« no previous file with comments | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698