| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python | |
| 2 | |
| 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 | |
| 5 # found in the LICENSE file. | |
| 6 | |
| 7 | |
| 8 """ | |
| 9 submit_try: Submit a try request. | |
| 10 | |
| 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. | |
| 13 """ | |
| 14 | |
| 15 | |
| 16 import httplib | |
| 17 import json | |
| 18 import os | |
| 19 import re | |
| 20 import shutil | |
| 21 import subprocess | |
| 22 import sys | |
| 23 import tempfile | |
| 24 | |
| 25 import retrieve_from_googlesource | |
| 26 | |
| 27 | |
| 28 # Alias which can be used to run a try on every builder. | |
| 29 ALL_BUILDERS = 'all' | |
| 30 # Alias which can be used to run a try on all compile builders. | |
| 31 COMPILE_BUILDERS = 'compile' | |
| 32 # Alias which can be used to run a try on all builders that are run in the CQ. | |
| 33 CQ_BUILDERS = 'cq' | |
| 34 # Alias which can be used to specify a regex to choose builders. | |
| 35 REGEX = 'regex' | |
| 36 | |
| 37 ALL_ALIASES = [ALL_BUILDERS, COMPILE_BUILDERS, REGEX, CQ_BUILDERS] | |
| 38 | |
| 39 LARGE_NUMBER_OF_BOTS = 5 | |
| 40 | |
| 41 GIT = 'git.bat' if os.name == 'nt' else 'git' | |
| 42 | |
| 43 # URL of the slaves.cfg file in the Skia buildbot sources. | |
| 44 SKIA_REPO = 'https://skia.googlesource.com/buildbot' | |
| 45 SLAVES_CFG_PATH = 'master/slaves.cfg' | |
| 46 | |
| 47 # All try builders have this suffix. | |
| 48 TRYBOT_SUFFIX = '-Trybot' | |
| 49 | |
| 50 # String for matching the svn url of the try server inside codereview.settings. | |
| 51 TRYSERVER_SVN_URL = 'TRYSERVER_SVN_URL: ' | |
| 52 | |
| 53 # Strings used for matching svn config properties. | |
| 54 URL_STR = 'URL' | |
| 55 REPO_ROOT_STR = 'Repository Root' | |
| 56 | |
| 57 | |
| 58 def FindDepotTools(): | |
| 59 """ Find depot_tools on the local machine and return its location. """ | |
| 60 which_cmd = 'where' if os.name == 'nt' else 'which' | |
| 61 cmd = [which_cmd, 'gcl'] | |
| 62 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
| 63 if proc.wait() != 0: | |
| 64 raise Exception('Couldn\'t find depot_tools in PATH!') | |
| 65 gcl = proc.communicate()[0].split('\n')[0].rstrip() | |
| 66 depot_tools_dir = os.path.dirname(gcl) | |
| 67 return depot_tools_dir | |
| 68 | |
| 69 | |
| 70 def GetCheckoutRoot(): | |
| 71 """ Determine where the local checkout is rooted.""" | |
| 72 cmd = ['git', 'rev-parse', '--show-toplevel'] | |
| 73 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, | |
| 74 stderr=subprocess.STDOUT) | |
| 75 if proc.wait() != 0: | |
| 76 raise Exception('Couldn\'t find checkout root!') | |
| 77 return os.path.basename(proc.communicate()[0]) | |
| 78 | |
| 79 | |
| 80 def GetTryRepo(): | |
| 81 """Determine the TRYSERVER_SVN_URL from the codereview.settings file.""" | |
| 82 codereview_settings_file = os.path.join(os.path.dirname(__file__), os.pardir, | |
| 83 'codereview.settings') | |
| 84 with open(codereview_settings_file) as f: | |
| 85 for line in f: | |
| 86 if line.startswith(TRYSERVER_SVN_URL): | |
| 87 return line[len(TRYSERVER_SVN_URL):].rstrip() | |
| 88 raise Exception('Couldn\'t determine the TRYSERVER_SVN_URL. Make sure it is ' | |
| 89 'defined in the %s file.' % codereview_settings_file) | |
| 90 | |
| 91 | |
| 92 def RetrieveTrybotList(): | |
| 93 """Retrieve the list of known trybots from the checked-in buildbot | |
| 94 configuration.""" | |
| 95 # Retrieve the slaves.cfg file from the repository. | |
| 96 slaves_cfg_text = retrieve_from_googlesource.get(SKIA_REPO, SLAVES_CFG_PATH) | |
| 97 | |
| 98 # Execute the slaves.cfg file to obtain the list of slaves. | |
| 99 vars = {} | |
| 100 exec(slaves_cfg_text, vars) | |
| 101 slaves_cfg = vars['slaves'] | |
| 102 | |
| 103 # Pull the list of known builders from the slaves list. | |
| 104 trybots = set() | |
| 105 for slave in slaves_cfg: | |
| 106 for builder in slave['builder']: | |
| 107 if not builder.endswith(TRYBOT_SUFFIX): | |
| 108 trybots.add(builder) | |
| 109 | |
| 110 return list(trybots), vars['cq_trybots'] | |
| 111 | |
| 112 | |
| 113 def ValidateArgs(argv, trybots, cq_trybots, is_svn=True): | |
| 114 """ Parse and validate command-line arguments. If the arguments are valid, | |
| 115 returns a tuple of (<changelist name>, <list of trybots>). | |
| 116 | |
| 117 trybots: list of strings; A list of the known try builders. | |
| 118 cq_trybots: list of strings; Trybots who get run by the commit queue. | |
| 119 is_svn: bool; whether or not we're in an svn checkout. | |
| 120 """ | |
| 121 | |
| 122 class CollectedArgs(object): | |
| 123 def __init__(self, bots, changelist, revision): | |
| 124 self._bots = bots | |
| 125 self._changelist = changelist | |
| 126 self._revision = revision | |
| 127 | |
| 128 @property | |
| 129 def bots(self): | |
| 130 for bot in self._bots: | |
| 131 yield bot | |
| 132 | |
| 133 @property | |
| 134 def changelist(self): | |
| 135 return self._changelist | |
| 136 | |
| 137 @property | |
| 138 def revision(self): | |
| 139 return self._revision | |
| 140 | |
| 141 usage = ( | |
| 142 """submit_try: Submit a try request. | |
| 143 submit_try %s--bot <buildername> [<buildername> ...] | |
| 144 | |
| 145 -b, --bot Builder(s) or Alias on which to run the try. Required. | |
| 146 Allowed aliases: %s | |
| 147 -h, --help Show this message. | |
| 148 -r <revision#> Revision from which to run the try. | |
| 149 -l, --list_bots List the available try builders and aliases and exit. | |
| 150 """ % ('<changelist> ' if is_svn else '', ALL_ALIASES)) | |
| 151 | |
| 152 def Error(msg=None): | |
| 153 if msg: | |
| 154 print msg | |
| 155 print usage | |
| 156 sys.exit(1) | |
| 157 | |
| 158 using_bots = None | |
| 159 changelist = None | |
| 160 revision = None | |
| 161 | |
| 162 while argv: | |
| 163 arg = argv.pop(0) | |
| 164 if arg == '-h' or arg == '--help': | |
| 165 Error() | |
| 166 elif arg == '-l' or arg == '--list_bots': | |
| 167 format_args = ['\n '.join(sorted(trybots))] + \ | |
| 168 ALL_ALIASES + \ | |
| 169 ['\n '.join(sorted(cq_trybots))] | |
| 170 print ( | |
| 171 """ | |
| 172 submit_try: Available builders:\n %s | |
| 173 | |
| 174 Can also use the following aliases to run on groups of builders- | |
| 175 %s: Will run against all trybots. | |
| 176 %s: Will run against all compile trybots. | |
| 177 %s: You will be prompted to enter a regex to select builders with. | |
| 178 %s: Will run against the same trybots as the commit queue:\n %s | |
| 179 | |
| 180 """ % tuple(format_args)) | |
| 181 sys.exit(0) | |
| 182 elif arg == '-b' or arg == '--bot': | |
| 183 if using_bots: | |
| 184 Error('--bot specified multiple times.') | |
| 185 if len(argv) < 1: | |
| 186 Error('You must specify a builder with "--bot".') | |
| 187 using_bots = [] | |
| 188 while argv and not argv[0].startswith('-'): | |
| 189 for bot in argv.pop(0).split(','): | |
| 190 if bot in ALL_ALIASES: | |
| 191 if using_bots: | |
| 192 Error('Cannot specify "%s" with additional builder names or ' | |
| 193 'aliases.' % bot) | |
| 194 elif bot == COMPILE_BUILDERS: | |
| 195 using_bots = [t for t in trybots if t.startswith('Build')] | |
| 196 elif bot == CQ_BUILDERS: | |
| 197 using_bots = cq_trybots | |
| 198 elif bot == REGEX: | |
| 199 while True: | |
| 200 regex = raw_input("Enter your trybot regex: ") | |
| 201 p = re.compile(regex) | |
| 202 using_bots = [t for t in trybots if p.match(t)] | |
| 203 print '\n\nTrybots that match your regex:\n%s\n\n' % '\n'.join( | |
| 204 using_bots) | |
| 205 if raw_input('Re-enter regex? [y,n]: ') == 'n': | |
| 206 break | |
| 207 break | |
| 208 else: | |
| 209 if not bot in trybots: | |
| 210 Error('Unrecognized builder: %s' % bot) | |
| 211 using_bots.append(bot) | |
| 212 elif arg == '-r': | |
| 213 if len(argv) < 1: | |
| 214 Error('You must specify a revision with "-r".') | |
| 215 revision = argv.pop(0) | |
| 216 else: | |
| 217 if changelist or not is_svn: | |
| 218 Error('Unknown argument: %s' % arg) | |
| 219 changelist = arg | |
| 220 if is_svn and not changelist: | |
| 221 Error('You must specify a changelist name.') | |
| 222 if not using_bots: | |
| 223 Error('You must specify one or more builders using --bot.') | |
| 224 if len(using_bots) > LARGE_NUMBER_OF_BOTS: | |
| 225 are_you_sure = raw_input('Running a try on a large number of bots is very ' | |
| 226 'expensive. You may be able to get enough ' | |
| 227 'information by running on a smaller set of bots. ' | |
| 228 'Are you sure you want to do this? [y,n]: ') | |
| 229 if are_you_sure != 'y': | |
| 230 Error() | |
| 231 return CollectedArgs(bots=using_bots, changelist=changelist, | |
| 232 revision=revision) | |
| 233 | |
| 234 | |
| 235 def SubmitTryRequest(trybots, revision=None): | |
| 236 """ Submits a try request on the given list of trybots. | |
| 237 | |
| 238 Args: | |
| 239 trybots: list of strings; the names of the try builders to run. | |
| 240 revision: optional string; the revision from which to run the try. | |
| 241 """ | |
| 242 botlist = ','.join(['%s%s' % (bot, TRYBOT_SUFFIX) for bot in trybots]) | |
| 243 # Find depot_tools. This is needed to import git_cl and trychange. | |
| 244 sys.path.append(FindDepotTools()) | |
| 245 import git_cl | |
| 246 import trychange | |
| 247 | |
| 248 cmd = [GIT, 'diff', git_cl.Changelist().GetUpstreamBranch(), | |
| 249 '--no-ext-diff'] | |
| 250 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| 251 git_data = proc.communicate() | |
| 252 if git_data[0] is None: | |
| 253 raise Exception('Failed to capture git diff!') | |
| 254 | |
| 255 temp_dir = tempfile.mkdtemp() | |
| 256 try: | |
| 257 diff_file = os.path.join(temp_dir, 'patch.diff') | |
| 258 with open(diff_file, 'wb') as f: | |
| 259 f.write(git_data[0]) | |
| 260 f.close() | |
| 261 | |
| 262 try_args = ['--use_svn', | |
| 263 '--svn_repo', GetTryRepo(), | |
| 264 '--root', GetCheckoutRoot(), | |
| 265 '--bot', botlist, | |
| 266 '--diff', diff_file, | |
| 267 ] | |
| 268 if revision: | |
| 269 try_args.extend(['-r', revision]) | |
| 270 | |
| 271 # Submit the try request. | |
| 272 trychange.TryChange(try_args, None, False) | |
| 273 finally: | |
| 274 shutil.rmtree(temp_dir) | |
| 275 | |
| 276 | |
| 277 def main(): | |
| 278 # Retrieve the list of active try builders from the build master. | |
| 279 trybots, cq_trybots = RetrieveTrybotList() | |
| 280 | |
| 281 # Determine if we're in an SVN checkout. | |
| 282 is_svn = os.path.isdir('.svn') | |
| 283 | |
| 284 # Parse and validate the command-line arguments. | |
| 285 args = ValidateArgs(sys.argv[1:], trybots=trybots, cq_trybots=cq_trybots, | |
| 286 is_svn=is_svn) | |
| 287 | |
| 288 # Submit the try request. | |
| 289 SubmitTryRequest(args.bots, args.revision) | |
| 290 | |
| 291 | |
| 292 if __name__ == '__main__': | |
| 293 sys.exit(main()) | |
| OLD | NEW |