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

Side by Side Diff: trychange.py

Issue 517005: After much refactory, finally add significant functionalities to trychanges.py (Closed)
Patch Set: Created 10 years, 12 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
« gclient_utils.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
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 posixpath
15 import shutil 16 import shutil
16 import socket 17 import socket
17 import subprocess 18 import subprocess
18 import sys 19 import sys
19 import tempfile 20 import tempfile
20 import urllib 21 import urllib
21 22
22 try: 23 try:
23 import breakpad 24 import breakpad
24 except ImportError: 25 except ImportError:
25 pass 26 pass
26 27
27 import gclient_utils 28 import gclient_utils
28 import scm 29 import scm
29 30
30 __version__ = '1.2' 31 __version__ = '1.2'
31 32
32 33
33 # Constants 34 # Constants
34 HELP_STRING = "Sorry, Tryserver is not available." 35 HELP_STRING = "Sorry, Tryserver is not available."
35 USAGE = r"""%prog [change_name] [options] 36 USAGE = r"""%prog [options]
36 37
37 Client-side script to send a try job to the try server. It communicates to 38 Client-side script to send a try job to the try server. It communicates to
38 the try server by either writting to a svn repository or by directly connecting 39 the try server by either writting to a svn repository or by directly connecting
39 to the server by HTTP. 40 to the server by HTTP.
40 41
41
42 Examples: 42 Examples:
43 Try a change against a particular revision: 43 Try a change against a particular revision:
44 %prog change_name -r 123 44 %prog change_name -r 123
45 45
46 A git patch off a web site (git inserts a/ and b/) and fix the base dir: 46 A git patch off a web site (git inserts a/ and b/) and fix the base dir:
47 %prog --url http://url/to/patch.diff --patchlevel 1 --root src 47 %prog --url http://url/to/patch.diff --patchlevel 1 --root src
48 48
49 Use svn to store the try job, specify an alternate email address and use a 49 Use svn to store the try job, specify an alternate email address and use a
50 premade diff file on the local drive: 50 premade diff file on the local drive:
51 %prog --email user@example.com 51 %prog --email user@example.com
52 --svn_repo svn://svn.chromium.org/chrome-try/try --diff foo.diff 52 --svn_repo svn://svn.chromium.org/chrome-try/try --diff foo.diff
53 53
54 Running only on a 'mac' slave with revision 123 and clobber first; specify 54 Running only on a 'mac' slave with revision 123 and clobber first; specify
55 manually the 3 source files to use for the try job: 55 manually the 3 source files to use for the try job:
56 %prog --bot mac --revision 123 --clobber -f src/a.cc -f src/a.h 56 %prog --bot mac --revision 123 --clobber -f src/a.cc -f src/a.h
57 -f include/b.h""" 57 -f include/b.h
58
59 When called from gcl, use the format gcl try <change_name>.
60 """
58 61
59 class InvalidScript(Exception): 62 class InvalidScript(Exception):
60 def __str__(self): 63 def __str__(self):
61 return self.args[0] + '\n' + HELP_STRING 64 return self.args[0] + '\n' + HELP_STRING
62 65
63 66
64 class NoTryServerAccess(Exception): 67 class NoTryServerAccess(Exception):
65 def __str__(self): 68 def __str__(self):
66 return self.args[0] + '\n' + HELP_STRING 69 return self.args[0] + '\n' + HELP_STRING
67 70
68 71
69 def EscapeDot(name): 72 def EscapeDot(name):
70 return name.replace('.', '-') 73 return name.replace('.', '-')
71 74
72 75
73 class SCM(object): 76 class SCM(object):
74 """Simplistic base class to implement one function: ProcessOptions.""" 77 """Simplistic base class to implement one function: ProcessOptions."""
75 def __init__(self, options, cwd): 78 def __init__(self, options, cwd):
76 self.checkout_root = cwd 79 self.checkout_root = cwd
77 self.options = options 80 self.options = options
78 self.files = self.options.files 81 self.files = self.options.files
79 self.options.files = None 82 self.options.files = None
83 self.codereview_settings = None
84 self.codereview_settings_file = 'codereview.settings'
80 85
81 def GetFileNames(self): 86 def GetFileNames(self):
82 """Return the list of files in the diff.""" 87 """Return the list of files in the diff."""
83 return self.files 88 return self.files
84 89
90 def GetCodeReviewSetting(self, key):
91 """Returns a value for the given key for this repository.
92
93 Uses gcl-style settings from the repository."""
94 if self.codereview_settings is None:
95 self.codereview_settings = {}
96 settings_file = self.ReadRootFile(self.codereview_settings_file)
97 if settings_file:
98 for line in settings_file.splitlines():
99 if not line or line.lstrip().startswith('#'):
100 continue
101 k, v = line.split(":", 1)
102 self.codereview_settings[k.strip()] = v.strip()
103 return self.codereview_settings.get(key, '')
104
105 def GclStyleSettings(self):
106 """Set default settings based on the gcl-style settings from the
107 repository."""
108 settings = {
109 'port': self.GetCodeReviewSetting('TRYSERVER_HTTP_PORT'),
110 'host': self.GetCodeReviewSetting('TRYSERVER_HTTP_HOST'),
111 'svn_repo': self.GetCodeReviewSetting('TRYSERVER_SVN_URL'),
112 'project': self.GetCodeReviewSetting('TRYSERVER_PROJECT'),
113 'root': self.GetCodeReviewSetting('TRYSERVER_ROOT'),
114 'patchlevel': self.GetCodeReviewSetting('TRYSERVER_PATCHLEVEL'),
115 }
116 for (k, v) in settings.iteritems():
117 if v and getattr(self.options, k) is None:
118 setattr(self.options, k, v)
119
120 def GclientStyleSettings(self):
121 """Find the root, assuming a gclient-style checkout."""
122 if not self.options.no_gclient and not self.options.root:
123 root = self.GetLocalRoot()
124 gclient_root = gclient_utils.FindGclientRoot(root)
125 if gclient_root:
126 self.options.root = gclient_utils.PathDifference(gclient_root, root)
127
128 def AutomagicalSettings(self):
129 """Determines settings based on supported code review and checkout tools.
130 """
131 self.GclStyleSettings()
132 self.GclientStyleSettings()
133
85 134
86 class SVN(SCM): 135 class SVN(SCM):
87 """Gathers the options and diff for a subversion checkout.""" 136 """Gathers the options and diff for a subversion checkout."""
88 def __init__(self, *args, **kwargs): 137 def __init__(self, *args, **kwargs):
89 SCM.__init__(self, *args, **kwargs) 138 SCM.__init__(self, *args, **kwargs)
90 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root) 139 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root)
91 if not self.options.email: 140 if not self.options.email:
92 # Assumes the svn credential is an email address. 141 # Assumes the svn credential is an email address.
93 self.options.email = scm.SVN.GetEmail(self.checkout_root) 142 self.options.email = scm.SVN.GetEmail(self.checkout_root)
94 143
144 def ReadRootFile(self, filename):
145 try:
146 # Try to search on the subversion repository for the file.
147 import gcl
148 data = gcl.GetCachedFile(filename, use_root=True)
149 logging.debug('%s:\n%s' % (filename, data))
150 return data
151 except ImportError:
152 try:
153 data = gclient_utils.FileRead(os.path.join(self.checkout_root,
154 filename))
155 logging.debug('%s:\n%s' % (filename, data))
156 return data
157 except (IOError, OSError):
158 logging.debug('%s:\nNone' % filename)
159 return None
160
95 def GenerateDiff(self): 161 def GenerateDiff(self):
96 """Returns a string containing the diff for the given file list. 162 """Returns a string containing the diff for the given file list.
97 163
98 The files in the list should either be absolute paths or relative to the 164 The files in the list should either be absolute paths or relative to the
99 given root. 165 given root.
100 """ 166 """
101 if not self.files: 167 if not self.files:
102 previous_cwd = os.getcwd() 168 previous_cwd = os.getcwd()
103 os.chdir(self.checkout_root) 169 os.chdir(self.checkout_root)
104 excluded = ['!', '?', 'X', ' ', '~'] 170 excluded = ['!', '?', 'X', ' ', '~']
105 self.files = [ 171 self.files = [
106 f[1] for f in scm.SVN.CaptureStatus(self.checkout_root) 172 f[1] for f in scm.SVN.CaptureStatus(self.checkout_root)
107 if f[0][0] not in excluded 173 if f[0][0] not in excluded
108 ] 174 ]
109 os.chdir(previous_cwd) 175 os.chdir(previous_cwd)
110 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True) 176 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True)
111 177
112 def GetLocalRoot(self): 178 def GetLocalRoot(self):
113 """Return the path of the repository root.""" 179 """Return the path of the repository root."""
114 return self.checkout_root 180 return self.checkout_root
115 181
116 def GetBots(self):
117 try:
118 # Try to search on the subversion repository for the file.
119 import gcl
120 return gcl.GetCachedFile('PRESUBMIT.py', use_root=True)
121 except ImportError:
122 try:
123 return gclient_utils.FileRead(os.path.join(self.checkout_root,
124 'PRESUBMIT.py'))
125 except (IOError, OSError):
126 return None
127
128 182
129 class GIT(SCM): 183 class GIT(SCM):
130 """Gathers the options and diff for a git checkout.""" 184 """Gathers the options and diff for a git checkout."""
131 def __init__(self, *args, **kwargs): 185 def __init__(self, *args, **kwargs):
132 SCM.__init__(self, *args, **kwargs) 186 SCM.__init__(self, *args, **kwargs)
133 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root) 187 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root)
134 if not self.options.name: 188 if not self.options.name:
135 self.options.name = scm.GIT.GetPatchName(self.checkout_root) 189 self.options.name = scm.GIT.GetPatchName(self.checkout_root)
136 if not self.options.email: 190 if not self.options.email:
137 self.options.email = scm.GIT.GetEmail(self.checkout_root) 191 self.options.email = scm.GIT.GetEmail(self.checkout_root)
138 192
193 def ReadRootFile(self, filename):
194 try:
195 # A git checkout is always a full checkout.
196 data = gclient_utils.FileRead(os.path.join(self.checkout_root, filename))
197 logging.debug('%s:\n%s' % (filename, data))
198 return data
199 except (IOError, OSError):
200 logging.debug('%s:\nNone' % filename)
201 return None
202
139 def GetLocalRoot(self): 203 def GetLocalRoot(self):
140 """Return the path of the repository root.""" 204 """Return the path of the repository root."""
141 return self.checkout_root 205 return self.checkout_root
142 206
143 def GenerateDiff(self): 207 def GenerateDiff(self):
144 # For now, ignores self.files 208 # For now, ignores self.files
145 return scm.GIT.GenerateDiff(self.checkout_root, full_move=True) 209 return scm.GIT.GenerateDiff(self.checkout_root, full_move=True)
146 210
147 def GetBots(self):
148 try:
149 # A git checkout is always a full checkout.
150 return gclient_utils.FileRead(os.path.join(self.checkout_root,
151 'PRESUBMIT.py'))
152 except (IOError, OSError):
153 return None
154
155 211
156 def _ParseSendChangeOptions(options): 212 def _ParseSendChangeOptions(options):
157 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" 213 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN."""
158 values = {} 214 values = {}
159 if options.email: 215 if options.email:
160 values['email'] = options.email 216 values['email'] = options.email
161 values['user'] = options.user 217 values['user'] = options.user
162 values['name'] = options.name 218 values['name'] = options.name
163 if options.bot: 219 if options.bot:
164 values['bot'] = ','.join(options.bot) 220 values['bot'] = ','.join(options.bot)
(...skipping 21 matching lines...) Expand all
186 def _SendChangeHTTP(options): 242 def _SendChangeHTTP(options):
187 """Send a change to the try server using the HTTP protocol.""" 243 """Send a change to the try server using the HTTP protocol."""
188 if not options.host: 244 if not options.host:
189 raise NoTryServerAccess('Please use the --host option to specify the try ' 245 raise NoTryServerAccess('Please use the --host option to specify the try '
190 'server host to connect to.') 246 'server host to connect to.')
191 if not options.port: 247 if not options.port:
192 raise NoTryServerAccess('Please use the --port option to specify the try ' 248 raise NoTryServerAccess('Please use the --port option to specify the try '
193 'server port to connect to.') 249 'server port to connect to.')
194 250
195 values = _ParseSendChangeOptions(options) 251 values = _ParseSendChangeOptions(options)
252 description = ''.join("%s=%s\n" % (k,v) for (k,v) in values.iteritems())
196 values['patch'] = options.diff 253 values['patch'] = options.diff
197 254
198 url = 'http://%s:%s/send_try_patch' % (options.host, options.port) 255 url = 'http://%s:%s/send_try_patch' % (options.host, options.port)
199 proxies = None 256 proxies = None
200 if options.proxy: 257 if options.proxy:
201 if options.proxy.lower() == 'none': 258 if options.proxy.lower() == 'none':
202 # Effectively disable HTTP_PROXY or Internet settings proxy setup. 259 # Effectively disable HTTP_PROXY or Internet settings proxy setup.
203 proxies = {} 260 proxies = {}
204 else: 261 else:
205 proxies = {'http': options.proxy, 'https': options.proxy} 262 proxies = {'http': options.proxy, 'https': options.proxy}
206 263
264 logging.info(description)
265 logging.info(url)
266 logging.info(options.diff)
207 if options.dry_run: 267 if options.dry_run:
208 # Last minute fake.
209 for (k,v) in values.iteritems():
210 if k != 'patch':
211 print("%s=%s" % (k,v))
212 print values['patch']
213 return 268 return
214 269
215 try: 270 try:
216 connection = urllib.urlopen(url, urllib.urlencode(values), proxies=proxies) 271 connection = urllib.urlopen(url, urllib.urlencode(values), proxies=proxies)
217 except IOError, e: 272 except IOError, e:
273 logging.warning(str(e))
218 if (values.get('bot') and len(e.args) > 2 and 274 if (values.get('bot') and len(e.args) > 2 and
219 e.args[2] == 'got a bad status line'): 275 e.args[2] == 'got a bad status line'):
220 raise NoTryServerAccess('%s is unaccessible. Bad --bot argument?' % url) 276 raise NoTryServerAccess('%s is unaccessible. Bad --bot argument?' % url)
221 else: 277 else:
222 raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url, 278 raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url,
223 str(e.args))) 279 str(e.args)))
224 if not connection: 280 if not connection:
225 raise NoTryServerAccess('%s is unaccessible.' % url) 281 raise NoTryServerAccess('%s is unaccessible.' % url)
226 response = connection.read() 282 response = connection.read()
227 if response != 'OK': 283 if response != 'OK':
228 raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response)) 284 raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response))
229 285
230 286
231 def _SendChangeSVN(options): 287 def _SendChangeSVN(options):
232 """Send a change to the try server by committing a diff file on a subversion 288 """Send a change to the try server by committing a diff file on a subversion
233 server.""" 289 server."""
234 if not options.svn_repo: 290 if not options.svn_repo:
235 raise NoTryServerAccess('Please use the --svn_repo option to specify the' 291 raise NoTryServerAccess('Please use the --svn_repo option to specify the'
236 ' try server svn repository to connect to.') 292 ' try server svn repository to connect to.')
237 293
238 values = _ParseSendChangeOptions(options) 294 values = _ParseSendChangeOptions(options)
239 description = '' 295 description = ''.join("%s=%s\n" % (k,v) for (k,v) in values.iteritems())
240 for (k,v) in values.iteritems(): 296 logging.info(description)
241 description += "%s=%s\n" % (k,v) 297 logging.info(options.svn_repo)
242 298 logging.info(options.diff)
243 if options.dry_run: 299 if options.dry_run:
244 # Last minute fake.
245 print str(descriptions)
246 print diff
247 return 300 return
248 301
249 # Do an empty checkout. 302 # Do an empty checkout.
250 temp_dir = tempfile.mkdtemp() 303 temp_dir = tempfile.mkdtemp()
251 temp_file = tempfile.NamedTemporaryFile() 304 temp_file = tempfile.NamedTemporaryFile()
252 try: 305 try:
253 try: 306 try:
254 command = ['svn', 'checkout', '--depth', 'empty', '-q', 307 command = ['svn', 'checkout', '--depth', 'empty', '-q',
255 options.svn_repo, temp_dir] 308 options.svn_repo, temp_dir]
256 if options.email: 309 if options.email:
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 This examines the current directory, guesses which SCM we're using, and 354 This examines the current directory, guesses which SCM we're using, and
302 returns an instance of the appropriate class. Exit with an error if we can't 355 returns an instance of the appropriate class. Exit with an error if we can't
303 figure it out. 356 figure it out.
304 357
305 Returns: 358 Returns:
306 A SCM instance. Exits if the SCM can't be guessed. 359 A SCM instance. Exits if the SCM can't be guessed.
307 """ 360 """
308 __pychecker__ = 'no-returnvalues' 361 __pychecker__ = 'no-returnvalues'
309 # Subversion has a .svn in all working directories. 362 # Subversion has a .svn in all working directories.
310 if os.path.isdir(os.path.join(cwd, '.svn')): 363 if os.path.isdir(os.path.join(cwd, '.svn')):
311 logging.info("Guessed VCS = Subversion") 364 logging.info("GuessVCS(%s) = Subversion" % cwd)
312 return SVN(options, cwd) 365 return SVN(options, cwd)
313 366
314 # Git has a command to test if you're in a git tree. 367 # Git has a command to test if you're in a git tree.
315 # Try running it, but don't die if we don't have git installed. 368 # Try running it, but don't die if we don't have git installed.
316 try: 369 try:
317 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"], cwd) 370 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"], cwd)
318 logging.info("Guessed VCS = Git") 371 logging.info("GuessVCS(%s) = Git" % cwd)
319 return GIT(options, cwd) 372 return GIT(options, cwd)
320 except gclient_utils.CheckCallError, e: 373 except gclient_utils.CheckCallError, e:
321 if e.retcode != 2: # ENOENT -- they don't have git installed. 374 if e.retcode != 2: # ENOENT -- they don't have git installed.
322 raise 375 raise
323 raise NoTryServerAccess("Could not guess version control system. " 376 raise NoTryServerAccess("Could not guess version control system. "
324 "Are you in a working copy directory?") 377 "Are you in a working copy directory?")
325 378
326 379
327 def TryChange(argv, 380 def TryChange(argv,
328 file_list, 381 file_list,
329 swallow_exception, 382 swallow_exception,
330 prog=None): 383 prog=None):
331 """ 384 """
332 Args: 385 Args:
333 argv: Arguments and options. 386 argv: Arguments and options.
334 file_list: Default value to pass to --file. 387 file_list: Default value to pass to --file.
335 swallow_exception: Whether we raise or swallow exceptions. 388 swallow_exception: Whether we raise or swallow exceptions.
336 """ 389 """
337 # Parse argv 390 # Parse argv
338 parser = optparse.OptionParser(usage=USAGE, 391 parser = optparse.OptionParser(usage=USAGE,
339 version=__version__, 392 version=__version__,
340 prog=prog) 393 prog=prog)
341 394 parser.add_option("-v", "--verbose", action="count", default=0,
395 help="Prints debugging infos")
342 group = optparse.OptionGroup(parser, "Result and status") 396 group = optparse.OptionGroup(parser, "Result and status")
343 group.add_option("-u", "--user", default=getpass.getuser(), 397 group.add_option("-u", "--user", default=getpass.getuser(),
344 help="Owner user name [default: %default]") 398 help="Owner user name [default: %default]")
345 group.add_option("-e", "--email", 399 group.add_option("-e", "--email",
346 default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS', 400 default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS',
347 os.environ.get('EMAIL_ADDRESS')), 401 os.environ.get('EMAIL_ADDRESS')),
348 help="Email address where to send the results. Use either " 402 help="Email address where to send the results. Use either "
349 "the TRYBOT_RESULTS_EMAIL_ADDRESS environment " 403 "the TRYBOT_RESULTS_EMAIL_ADDRESS environment "
350 "variable or EMAIL_ADDRESS to set the email address " 404 "variable or EMAIL_ADDRESS to set the email address "
351 "the try bots report results to [default: %default]") 405 "the try bots report results to [default: %default]")
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 "try, relative to the repository root") 446 "try, relative to the repository root")
393 group.add_option("--diff", 447 group.add_option("--diff",
394 help="File containing the diff to try") 448 help="File containing the diff to try")
395 group.add_option("--url", 449 group.add_option("--url",
396 help="Url where to grab a patch") 450 help="Url where to grab a patch")
397 group.add_option("--root", 451 group.add_option("--root",
398 help="Root to use for the patch; base subdirectory for " 452 help="Root to use for the patch; base subdirectory for "
399 "patch created in a subdirectory") 453 "patch created in a subdirectory")
400 group.add_option("--patchlevel", type='int', metavar="LEVEL", 454 group.add_option("--patchlevel", type='int', metavar="LEVEL",
401 help="Used as -pN parameter to patch") 455 help="Used as -pN parameter to patch")
402 group.add_option("--sub_rep", action="append", default=["."], 456 group.add_option("--sub_rep", action="append", default=[],
403 help="Subcheckout to use in addition. This is mainly " 457 help="Subcheckout to use in addition. This is mainly "
404 "useful for gclient-style checkouts.") 458 "useful for gclient-style checkouts.")
459 group.add_option("--no_gclient", action="store_true",
460 help="Disable automatic search for gclient checkout.")
405 parser.add_option_group(group) 461 parser.add_option_group(group)
406 462
407 group = optparse.OptionGroup(parser, "Access the try server by HTTP") 463 group = optparse.OptionGroup(parser, "Access the try server by HTTP")
408 group.add_option("--use_http", 464 group.add_option("--use_http",
409 action="store_const", 465 action="store_const",
410 const=_SendChangeHTTP, 466 const=_SendChangeHTTP,
411 dest="send_patch", 467 dest="send_patch",
412 help="Use HTTP to talk to the try server [default]") 468 help="Use HTTP to talk to the try server [default]")
413 group.add_option("--host", 469 group.add_option("--host",
414 help="Host address") 470 help="Host address")
(...skipping 12 matching lines...) Expand all
427 group.add_option("--svn_repo", 483 group.add_option("--svn_repo",
428 metavar="SVN_URL", 484 metavar="SVN_URL",
429 help="SVN url to use to write the changes in; --use_svn is " 485 help="SVN url to use to write the changes in; --use_svn is "
430 "implied when using --svn_repo") 486 "implied when using --svn_repo")
431 parser.add_option_group(group) 487 parser.add_option_group(group)
432 488
433 options, args = parser.parse_args(argv) 489 options, args = parser.parse_args(argv)
434 if len(args) == 1 and args[0] == 'help': 490 if len(args) == 1 and args[0] == 'help':
435 parser.print_help() 491 parser.print_help()
436 492
437 # Switch the default accordingly if there was no default send_patch. 493 if options.verbose == 0:
438 if not options.send_patch: 494 logging.basicConfig(level=logging.ERROR)
439 if options.port and options.host: 495 elif options.verbose == 1:
440 options.send_patch = _SendChangeHTTP 496 logging.basicConfig(level=logging.WARNING)
441 elif options.svn_repo: 497 elif options.verbose == 2:
442 options.send_patch = _SendChangeSVN 498 logging.basicConfig(level=logging.INFO)
443 else: 499 elif options.verbose > 2:
444 parser.error('Please specify an access method.') 500 logging.basicConfig(level=logging.DEBUG)
445 501
446 try: 502 try:
447 # Process the VCS in any case at least to retrieve the email address. 503 # Always include os.getcwd() in the checkout settings.
448 checkouts = [] 504 checkouts = []
505 checkouts.append(GuessVCS(options, os.getcwd()))
506 checkouts[0].AutomagicalSettings()
449 for item in options.sub_rep: 507 for item in options.sub_rep:
450 checkout = GuessVCS(options, item) 508 checkout = GuessVCS(options, item)
451 if checkout.GetLocalRoot() in [c.GetLocalRoot() for c in checkouts]: 509 if checkout.GetLocalRoot() in [c.GetLocalRoot() for c in checkouts]:
452 parser.error('Specified the root %s two times.' % 510 parser.error('Specified the root %s two times.' %
453 checkout.GetLocalRoot()) 511 checkout.GetLocalRoot())
454 checkouts.append(checkout) 512 checkouts.append(checkout)
455 513
514 # If there was no transport selected yet, now we must have enough data to
515 # select one.
516 if not options.send_patch:
517 if options.port and options.host:
518 options.send_patch = _SendChangeHTTP
519 elif options.svn_repo:
520 options.send_patch = _SendChangeSVN
521 else:
522 parser.error('Please specify an access method.')
523
456 # Convert options.diff into the content of the diff. 524 # Convert options.diff into the content of the diff.
457 if options.url: 525 if options.url:
458 if options.files: 526 if options.files:
459 parser.error('You cannot specify files and --url at the same time.') 527 parser.error('You cannot specify files and --url at the same time.')
460 options.diff = urllib.urlopen(options.url).read() 528 options.diff = urllib.urlopen(options.url).read()
461 elif options.diff: 529 elif options.diff:
462 if options.files: 530 if options.files:
463 parser.error('You cannot specify files and --diff at the same time.') 531 parser.error('You cannot specify files and --diff at the same time.')
464 options.diff = gclient_utils.FileRead(options.diff, 'rb') 532 options.diff = gclient_utils.FileRead(options.diff, 'rb')
465 else: 533 else:
466 # Use this as the base. 534 # Use this as the base.
467 root = checkouts[0].GetLocalRoot() 535 root = checkouts[0].GetLocalRoot()
468 diffs = [] 536 diffs = []
469 for checkout in checkouts: 537 for checkout in checkouts:
470 diff = checkout.GenerateDiff().splitlines(True) 538 diff = checkout.GenerateDiff().splitlines(True)
471 # Munge it. 539 # Munge it.
472 path_diff = gclient_utils.PathDifference(root, checkout.GetLocalRoot()) 540 path_diff = gclient_utils.PathDifference(root, checkout.GetLocalRoot())
473 for i in range(len(diff)): 541 for i in range(len(diff)):
474 if diff[i].startswith('--- ') or diff[i].startswith('+++ '): 542 if diff[i].startswith('--- ') or diff[i].startswith('+++ '):
475 diff[i] = diff[i][0:3] + path_diff + diff[i][4:] 543 diff[i] = diff[i][0:4] + posixpath.join(path_diff, diff[i][4:])
476 diffs.extend(diff) 544 diffs.extend(diff)
477 options.diff = ''.join(diffs) 545 options.diff = ''.join(diffs)
478 546
479 if not options.bot: 547 if not options.bot:
480 # Get try slaves from PRESUBMIT.py files if not specified. 548 # Get try slaves from PRESUBMIT.py files if not specified.
481 # Even if the diff comes from options.url, use the local checkout for bot 549 # Even if the diff comes from options.url, use the local checkout for bot
482 # selection. 550 # selection.
483 try: 551 try:
484 import presubmit_support 552 import presubmit_support
485 root_presubmit = checkouts[0].GetBots() 553 root_presubmit = checkouts[0].ReadRootFile('PRESUBMIT.py')
486 options.bot = presubmit_support.DoGetTrySlaves( 554 options.bot = presubmit_support.DoGetTrySlaves(
487 checkouts[0].GetFileNames(), 555 checkouts[0].GetFileNames(),
488 checkouts[0].GetLocalRoot(), 556 checkouts[0].GetLocalRoot(),
489 root_presubmit, 557 root_presubmit,
490 False, 558 False,
491 sys.stdout) 559 sys.stdout)
492 except ImportError: 560 except ImportError:
493 pass 561 pass
494 # If no bot is specified, either the default pool will be selected or the 562 # If no bot is specified, either the default pool will be selected or the
495 # try server will refuse the job. Either case we don't need to interfere. 563 # try server will refuse the job. Either case we don't need to interfere.
(...skipping 20 matching lines...) Expand all
516 except (InvalidScript, NoTryServerAccess), e: 584 except (InvalidScript, NoTryServerAccess), e:
517 if swallow_exception: 585 if swallow_exception:
518 return 1 586 return 1
519 print e 587 print e
520 return 1 588 return 1
521 return 0 589 return 0
522 590
523 591
524 if __name__ == "__main__": 592 if __name__ == "__main__":
525 sys.exit(TryChange(None, [], False)) 593 sys.exit(TryChange(None, [], False))
OLDNEW
« gclient_utils.py ('K') | « tests/trychange_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698