| 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 | 
|---|