| 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 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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)) |
| OLD | NEW |