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 |