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 |