| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 | 7 |
| 8 """ | 8 """ |
| 9 submit_try: Submit a try request. | 9 submit_try: Submit a try request. |
| 10 | 10 |
| 11 This is a thin wrapper around the try request utilities in depot_tools which | 11 This is a thin wrapper around the try request utilities in depot_tools which |
| 12 adds some validation and supports both git and svn. | 12 adds some validation and supports both git and svn. |
| 13 """ | 13 """ |
| 14 | 14 |
| 15 | 15 |
| 16 import httplib | 16 import httplib |
| 17 import json | 17 import json |
| 18 import os | 18 import os |
| 19 import re | 19 import re |
| 20 import shutil | 20 import shutil |
| 21 import subprocess | 21 import subprocess |
| 22 import svn | 22 import svn |
| 23 import sys | 23 import sys |
| 24 import tempfile | 24 import tempfile |
| 25 | 25 |
| 26 import buildbot_globals | 26 import retrieve_from_googlesource |
| 27 | 27 |
| 28 | 28 |
| 29 # Alias which can be used to run a try on every builder. | 29 # Alias which can be used to run a try on every builder. |
| 30 ALL_BUILDERS = 'all' | 30 ALL_BUILDERS = 'all' |
| 31 # Alias which can be used to run a try on all compile builders. | 31 # Alias which can be used to run a try on all compile builders. |
| 32 COMPILE_BUILDERS = 'compile' | 32 COMPILE_BUILDERS = 'compile' |
| 33 # Alias which can be used to run a try on all builders that are run in the CQ. | 33 # Alias which can be used to run a try on all builders that are run in the CQ. |
| 34 CQ_BUILDERS = 'cq' | 34 CQ_BUILDERS = 'cq' |
| 35 # Alias which can be used to specify a regex to choose builders. | 35 # Alias which can be used to specify a regex to choose builders. |
| 36 REGEX = 'regex' | 36 REGEX = 'regex' |
| 37 | 37 |
| 38 ALL_ALIASES = [ALL_BUILDERS, COMPILE_BUILDERS, REGEX, CQ_BUILDERS] | 38 ALL_ALIASES = [ALL_BUILDERS, COMPILE_BUILDERS, REGEX, CQ_BUILDERS] |
| 39 | 39 |
| 40 LARGE_NUMBER_OF_BOTS = 5 |
| 41 |
| 40 GIT = 'git.bat' if os.name == 'nt' else 'git' | 42 GIT = 'git.bat' if os.name == 'nt' else 'git' |
| 41 | 43 |
| 42 # URL of the slaves.cfg file in the Skia buildbot sources. | 44 # URL of the slaves.cfg file in the Skia buildbot sources. |
| 43 SLAVES_CFG_URL = ('https://skia.googlesource.com/buildbot/+/master/' | 45 SKIA_REPO = 'https://skia.googlesource.com/buildbot' |
| 44 'master/slaves.cfg') | 46 SLAVES_CFG_PATH = 'master/slaves.cfg' |
| 45 | 47 |
| 46 # All try builders have this suffix. | 48 # All try builders have this suffix. |
| 47 TRYBOT_SUFFIX = '-Trybot' | 49 TRYBOT_SUFFIX = '-Trybot' |
| 48 | 50 |
| 49 # String for matching the svn url of the try server inside codereview.settings. | 51 # String for matching the svn url of the try server inside codereview.settings. |
| 50 TRYSERVER_SVN_URL = 'TRYSERVER_SVN_URL: ' | 52 TRYSERVER_SVN_URL = 'TRYSERVER_SVN_URL: ' |
| 51 | 53 |
| 52 # Strings used for matching svn config properties. | 54 # Strings used for matching svn config properties. |
| 53 URL_STR = 'URL' | 55 URL_STR = 'URL' |
| 54 REPO_ROOT_STR = 'Repository Root' | 56 REPO_ROOT_STR = 'Repository Root' |
| 55 | 57 |
| 56 | 58 |
| 57 def FindDepotTools(): | 59 def FindDepotTools(): |
| 58 """ Find depot_tools on the local machine and return its location. """ | 60 """ Find depot_tools on the local machine and return its location. """ |
| 59 which_cmd = 'where' if os.name == 'nt' else 'which' | 61 which_cmd = 'where' if os.name == 'nt' else 'which' |
| 60 cmd = [which_cmd, 'gcl'] | 62 cmd = [which_cmd, 'gcl'] |
| 61 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | 63 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
| 62 if proc.wait() != 0: | 64 if proc.wait() != 0: |
| 63 raise Exception('Couldn\'t find depot_tools in PATH!') | 65 raise Exception('Couldn\'t find depot_tools in PATH!') |
| 64 gcl = proc.communicate()[0].split('\n')[0].rstrip() | 66 gcl = proc.communicate()[0].split('\n')[0].rstrip() |
| 65 depot_tools_dir = os.path.dirname(gcl) | 67 depot_tools_dir = os.path.dirname(gcl) |
| 66 return depot_tools_dir | 68 return depot_tools_dir |
| 67 | 69 |
| 68 | 70 |
| 69 def GetCheckoutRoot(is_svn=True): | 71 def GetCheckoutRoot(): |
| 70 """ Determine where the local checkout is rooted. | 72 """ Determine where the local checkout is rooted.""" |
| 71 | 73 cmd = ['git', 'rev-parse', '--show-toplevel'] |
| 72 is_svn: boolean; whether we're in an SVN checkout. If False, assume we're in | 74 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, |
| 73 a git checkout. | 75 stderr=subprocess.STDOUT) |
| 74 """ | 76 if proc.wait() != 0: |
| 75 if is_svn: | 77 raise Exception('Couldn\'t find checkout root!') |
| 76 repo = svn.Svn(os.curdir) | 78 return os.path.basename(proc.communicate()[0]) |
| 77 svn_info = repo.GetInfo() | |
| 78 url = svn_info.get(URL_STR, None) | |
| 79 repo_root = svn_info.get(REPO_ROOT_STR, None) | |
| 80 if not url or not repo_root: | |
| 81 raise Exception('Couldn\'t find checkout root!') | |
| 82 if url == repo_root: | |
| 83 return 'svn' | |
| 84 return url[len(repo_root)+1:] | |
| 85 else: | |
| 86 cmd = ['git', 'rev-parse', '--show-toplevel'] | |
| 87 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, | |
| 88 stderr=subprocess.STDOUT) | |
| 89 if proc.wait() != 0: | |
| 90 raise Exception('Couldn\'t find checkout root!') | |
| 91 return os.path.basename(proc.communicate()[0]) | |
| 92 | 79 |
| 93 | 80 |
| 94 def GetTryRepo(): | 81 def GetTryRepo(): |
| 95 """Determine the TRYSERVER_SVN_URL from the codereview.settings file.""" | 82 """Determine the TRYSERVER_SVN_URL from the codereview.settings file.""" |
| 96 codereview_settings_file = os.path.join(os.path.dirname(__file__), os.pardir, | 83 codereview_settings_file = os.path.join(os.path.dirname(__file__), os.pardir, |
| 97 'codereview.settings') | 84 'codereview.settings') |
| 98 with open(codereview_settings_file) as f: | 85 with open(codereview_settings_file) as f: |
| 99 for line in f: | 86 for line in f: |
| 100 if line.startswith(TRYSERVER_SVN_URL): | 87 if line.startswith(TRYSERVER_SVN_URL): |
| 101 return line[len(TRYSERVER_SVN_URL):].rstrip() | 88 return line[len(TRYSERVER_SVN_URL):].rstrip() |
| 102 raise Exception('Couldn\'t determine the TRYSERVER_SVN_URL. Make sure it is ' | 89 raise Exception('Couldn\'t determine the TRYSERVER_SVN_URL. Make sure it is ' |
| 103 'defined in the %s file.' % codereview_settings_file) | 90 'defined in the %s file.' % codereview_settings_file) |
| 104 | 91 |
| 105 | 92 |
| 106 def RetrieveTrybotList(): | 93 def RetrieveTrybotList(): |
| 107 """Retrieve the list of known trybots from the checked-in buildbot | 94 """Retrieve the list of known trybots from the checked-in buildbot |
| 108 configuration.""" | 95 configuration.""" |
| 109 # Retrieve the slaves.cfg file from the repository. | 96 # Retrieve the slaves.cfg file from the repository. |
| 110 slaves_cfg_text = buildbot_globals.retrieve_from_googlesource(SLAVES_CFG_URL) | 97 slaves_cfg_text = retrieve_from_googlesource.get(SKIA_REPO, SLAVES_CFG_PATH) |
| 111 | 98 |
| 112 # Execute the slaves.cfg file to obtain the list of slaves. | 99 # Execute the slaves.cfg file to obtain the list of slaves. |
| 113 vars = {} | 100 vars = {} |
| 114 exec(slaves_cfg_text, vars) | 101 exec(slaves_cfg_text, vars) |
| 115 slaves_cfg = vars['slaves'] | 102 slaves_cfg = vars['slaves'] |
| 116 | 103 |
| 117 # Pull the list of known builders from the slaves list. | 104 # Pull the list of known builders from the slaves list. |
| 118 trybots = set() | 105 trybots = set() |
| 119 for slave in slaves_cfg: | 106 for slave in slaves_cfg: |
| 120 for builder in slave['builder']: | 107 for builder in slave['builder']: |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 198 Error('--bot specified multiple times.') | 185 Error('--bot specified multiple times.') |
| 199 if len(argv) < 1: | 186 if len(argv) < 1: |
| 200 Error('You must specify a builder with "--bot".') | 187 Error('You must specify a builder with "--bot".') |
| 201 using_bots = [] | 188 using_bots = [] |
| 202 while argv and not argv[0].startswith('-'): | 189 while argv and not argv[0].startswith('-'): |
| 203 for bot in argv.pop(0).split(','): | 190 for bot in argv.pop(0).split(','): |
| 204 if bot in ALL_ALIASES: | 191 if bot in ALL_ALIASES: |
| 205 if using_bots: | 192 if using_bots: |
| 206 Error('Cannot specify "%s" with additional builder names or ' | 193 Error('Cannot specify "%s" with additional builder names or ' |
| 207 'aliases.' % bot) | 194 'aliases.' % bot) |
| 208 if bot == ALL_BUILDERS: | |
| 209 are_you_sure = raw_input('Running a try on every bot is very ' | |
| 210 'expensive. You may be able to get ' | |
| 211 'enough information by running on a ' | |
| 212 'smaller set of bots. Are you sure you ' | |
| 213 'want to run your try job on all of the ' | |
| 214 'trybots? [y,n]: ') | |
| 215 if are_you_sure == 'y': | |
| 216 using_bots = trybots | |
| 217 elif bot == COMPILE_BUILDERS: | 195 elif bot == COMPILE_BUILDERS: |
| 218 using_bots = [t for t in trybots if t.startswith('Build')] | 196 using_bots = [t for t in trybots if t.startswith('Build')] |
| 219 elif bot == CQ_BUILDERS: | 197 elif bot == CQ_BUILDERS: |
| 220 using_bots = cq_trybots | 198 using_bots = cq_trybots |
| 221 elif bot == REGEX: | 199 elif bot == REGEX: |
| 222 while True: | 200 while True: |
| 223 regex = raw_input("Enter your trybot regex: ") | 201 regex = raw_input("Enter your trybot regex: ") |
| 224 p = re.compile(regex) | 202 p = re.compile(regex) |
| 225 using_bots = [t for t in trybots if p.match(t)] | 203 using_bots = [t for t in trybots if p.match(t)] |
| 226 print '\n\nTrybots that match your regex:\n%s\n\n' % '\n'.join( | 204 print '\n\nTrybots that match your regex:\n%s\n\n' % '\n'.join( |
| (...skipping 10 matching lines...) Expand all Loading... |
| 237 Error('You must specify a revision with "-r".') | 215 Error('You must specify a revision with "-r".') |
| 238 revision = argv.pop(0) | 216 revision = argv.pop(0) |
| 239 else: | 217 else: |
| 240 if changelist or not is_svn: | 218 if changelist or not is_svn: |
| 241 Error('Unknown argument: %s' % arg) | 219 Error('Unknown argument: %s' % arg) |
| 242 changelist = arg | 220 changelist = arg |
| 243 if is_svn and not changelist: | 221 if is_svn and not changelist: |
| 244 Error('You must specify a changelist name.') | 222 Error('You must specify a changelist name.') |
| 245 if not using_bots: | 223 if not using_bots: |
| 246 Error('You must specify one or more builders using --bot.') | 224 Error('You must specify one or more builders using --bot.') |
| 225 if len(using_bots) > LARGE_NUMBER_OF_BOTS: |
| 226 are_you_sure = raw_input('Running a try on a large number of bots is very ' |
| 227 'expensive. You may be able to get enough ' |
| 228 'information by running on a smaller set of bots. ' |
| 229 'Are you sure you want to do this? [y,n]: ') |
| 230 if are_you_sure != 'y': |
| 231 Error() |
| 247 return CollectedArgs(bots=using_bots, changelist=changelist, | 232 return CollectedArgs(bots=using_bots, changelist=changelist, |
| 248 revision=revision) | 233 revision=revision) |
| 249 | 234 |
| 250 | 235 |
| 251 def SubmitTryRequest(args, is_svn=True): | 236 def SubmitTryRequest(trybots, revision=None): |
| 252 """ Submits a try request for the given changelist on the given list of | 237 """ Submits a try request on the given list of trybots. |
| 253 trybots. | |
| 254 | 238 |
| 255 args: Object whose properties are derived from command-line arguments. If | 239 Args: |
| 256 is_svn is True, it should contain: | 240 trybots: list of strings; the names of the try builders to run. |
| 257 - changelist: string; the name of the changelist to try. | 241 revision: optional string; the revision from which to run the try. |
| 258 - bot: list of strings; the names of the try builders to run. | |
| 259 - revision: optional, int; the revision number from which to run the try. | |
| 260 If is_svn is False, it should contain: | |
| 261 - bot: list of strings; the names of the try builders to run. | |
| 262 - revision: optional, int; the revision number from which to run the try. | |
| 263 is_svn: boolean; are we in an SVN repo? | |
| 264 """ | 242 """ |
| 265 botlist = ','.join(['%s%s' % (bot, TRYBOT_SUFFIX) for bot in args.bots]) | 243 botlist = ','.join(['%s%s' % (bot, TRYBOT_SUFFIX) for bot in trybots]) |
| 266 if is_svn: | 244 # Find depot_tools. This is needed to import git_cl and trychange. |
| 267 gcl_cmd = 'gcl.bat' if os.name == 'nt' else 'gcl' | 245 sys.path.append(FindDepotTools()) |
| 268 try_args = [gcl_cmd, 'try', args.changelist, | 246 import git_cl |
| 269 '--root', GetCheckoutRoot(is_svn), | 247 import trychange |
| 270 '--bot', botlist] | |
| 271 if args.revision: | |
| 272 try_args.extend(['-r', args.revision]) | |
| 273 print ' '.join(try_args) | |
| 274 proc = subprocess.Popen(try_args, stdout=subprocess.PIPE, | |
| 275 stderr=subprocess.STDOUT) | |
| 276 if proc.wait() != 0: | |
| 277 raise Exception('Failed to submit try request: %s' % ( | |
| 278 proc.communicate()[0])) | |
| 279 print proc.communicate()[0] | |
| 280 else: | |
| 281 # Find depot_tools. This is needed to import git_cl and trychange. | |
| 282 sys.path.append(FindDepotTools()) | |
| 283 import git_cl | |
| 284 import trychange | |
| 285 | 248 |
| 286 cmd = [GIT, 'diff', git_cl.Changelist().GetUpstreamBranch(), | 249 cmd = [GIT, 'diff', git_cl.Changelist().GetUpstreamBranch(), |
| 287 '--no-ext-diff'] | 250 '--no-ext-diff'] |
| 288 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 251 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 289 git_data = proc.communicate() | 252 git_data = proc.communicate() |
| 290 if git_data[0] is None: | 253 if git_data[0] is None: |
| 291 raise Exception('Failed to capture git diff!') | 254 raise Exception('Failed to capture git diff!') |
| 292 | 255 |
| 293 temp_dir = tempfile.mkdtemp() | 256 temp_dir = tempfile.mkdtemp() |
| 294 try: | 257 try: |
| 295 diff_file = os.path.join(temp_dir, 'patch.diff') | 258 diff_file = os.path.join(temp_dir, 'patch.diff') |
| 296 with open(diff_file, 'wb') as f: | 259 with open(diff_file, 'wb') as f: |
| 297 f.write(git_data[0]) | 260 f.write(git_data[0]) |
| 298 f.close() | 261 f.close() |
| 299 | 262 |
| 300 try_args = ['--use_svn', | 263 try_args = ['--use_svn', |
| 301 '--svn_repo', GetTryRepo(), | 264 '--svn_repo', GetTryRepo(), |
| 302 '--root', GetCheckoutRoot(is_svn), | 265 '--root', GetCheckoutRoot(), |
| 303 '--bot', botlist, | 266 '--bot', botlist, |
| 304 '--diff', diff_file, | 267 '--diff', diff_file, |
| 305 ] | 268 ] |
| 306 if args.revision: | 269 if revision: |
| 307 try_args.extend(['-r', args.revision]) | 270 try_args.extend(['-r', revision]) |
| 308 | 271 |
| 309 # Submit the try request. | 272 # Submit the try request. |
| 310 trychange.TryChange(try_args, None, False) | 273 trychange.TryChange(try_args, None, False) |
| 311 finally: | 274 finally: |
| 312 shutil.rmtree(temp_dir) | 275 shutil.rmtree(temp_dir) |
| 313 | 276 |
| 314 | 277 |
| 315 def main(): | 278 def main(): |
| 316 # Retrieve the list of active try builders from the build master. | 279 # Retrieve the list of active try builders from the build master. |
| 317 trybots, cq_trybots = RetrieveTrybotList() | 280 trybots, cq_trybots = RetrieveTrybotList() |
| 318 | 281 |
| 319 # Determine if we're in an SVN checkout. | 282 # Determine if we're in an SVN checkout. |
| 320 is_svn = os.path.isdir('.svn') | 283 is_svn = os.path.isdir('.svn') |
| 321 | 284 |
| 322 # Parse and validate the command-line arguments. | 285 # Parse and validate the command-line arguments. |
| 323 args = ValidateArgs(sys.argv[1:], trybots=trybots, cq_trybots=cq_trybots, | 286 args = ValidateArgs(sys.argv[1:], trybots=trybots, cq_trybots=cq_trybots, |
| 324 is_svn=is_svn) | 287 is_svn=is_svn) |
| 325 | 288 |
| 326 # Submit the try request. | 289 # Submit the try request. |
| 327 SubmitTryRequest(args, is_svn=is_svn) | 290 SubmitTryRequest(args.bots, args.revision) |
| 328 | 291 |
| 329 | 292 |
| 330 if __name__ == '__main__': | 293 if __name__ == '__main__': |
| 331 sys.exit(main()) | 294 sys.exit(main()) |
| OLD | NEW |