blob: 2d18fc653f651151e476f64ec863bc34c813de5e [file] [log] [blame]
borenet@google.com6b5388e2013-01-23 20:54:29 +00001#!/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"""
9submit_try: Submit a try request.
10
11This is a thin wrapper around the try request utilities in depot_tools which
12adds some validation and supports both git and svn.
13"""
14
15
16import argparse
17import httplib
18import json
19import os
20import subprocess
21import sys
22
23
24# Alias which can be used to run a try on every builder.
25ALL_BUILDERS = 'all'
26
27# Contact information for the build master.
28# TODO(borenet): Share this information from a single location. Filed bug:
29# http://code.google.com/p/skia/issues/detail?id=1081
30SKIA_BUILD_MASTER_HOST = '70.32.156.51'
31SKIA_BUILD_MASTER_PORT = '10117'
32
33# All try builders have this suffix.
34TRYBOT_SUFFIX = '_Trybot'
35
36# Location of the codereview.settings file in the Skia repo.
37SKIA_URL = 'skia.googlecode.com'
38CODEREVIEW_SETTINGS = '/svn/codereview.settings'
39
40# String for matching the svn url of the try server inside codereview.settings.
41TRYSERVER_SVN_URL = 'TRYSERVER_SVN_URL: '
42
43# Strings used for matching svn config properties.
44URL_STR = 'URL: '
45REPO_ROOT_STR = 'Repository Root: '
46
47
48def FindDepotTools():
49 """ Find depot_tools on the local machine and return its location. """
50 which_cmd = 'where' if os.name == 'nt' else 'which'
51 cmd = [which_cmd, 'gcl']
52 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
53 if proc.wait() != 0:
54 raise Exception('Couldn\'t find depot_tools in PATH!')
borenet@google.com6c55b512013-01-24 21:38:51 +000055 gcl = proc.communicate()[0].split('\n')[0].rstrip()
borenet@google.com6b5388e2013-01-23 20:54:29 +000056 depot_tools_dir = os.path.dirname(gcl)
57 return depot_tools_dir
58
59
60def GetCheckoutRoot(is_svn=True):
61 """ Determine where the local checkout is rooted.
62
63 is_svn: boolean; whether we're in an SVN checkout. If False, assume we're in
64 a git checkout.
65 """
66 if is_svn:
borenet@google.com6c55b512013-01-24 21:38:51 +000067 svn_cmd = 'svn.bat' if os.name == 'nt' else 'svn'
68 cmd = [svn_cmd, 'info']
borenet@google.com6b5388e2013-01-23 20:54:29 +000069 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
70 stderr=subprocess.STDOUT)
71 if proc.wait() != 0:
72 raise Exception('Couldn\'t find checkout root!')
73 output = proc.communicate()[0].split('\n')
74 url = None
75 repo_root = None
76 for line in output:
77 if line.startswith(REPO_ROOT_STR):
78 repo_root = line[len(REPO_ROOT_STR):].rstrip()
79 elif line.startswith(URL_STR):
80 url = line[len(URL_STR):].rstrip()
81 if not url or not repo_root:
82 raise Exception('Couldn\'t find checkout root!')
83 if url == repo_root:
84 return 'svn'
85 return url[len(repo_root)+1:]
86 else:
87 cmd = ['git', 'rev-parse', '--show-toplevel']
88 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
89 stderr=subprocess.STDOUT)
90 if proc.wait() != 0:
91 raise Exception('Couldn\'t find checkout root!')
92 return os.path.basename(proc.communicate()[0])
93
94
95def GetTryRepo():
96 """ Determine the TRYSERVER_SVN_URL from the codereview.settings file in the
97 Skia repo. """
98 connection = httplib.HTTPConnection(SKIA_URL)
99 connection.request('GET', CODEREVIEW_SETTINGS)
100 content = connection.getresponse().read()
101 for line in content.split('\n'):
102 if line.startswith(TRYSERVER_SVN_URL):
103 return line[len(TRYSERVER_SVN_URL):].rstrip()
104 raise Exception('Couldn\'t determine the TRYSERVER_SVN_URL. Make sure it is '
105 'defined in the %s file.' % CODEREVIEW_SETTINGS)
106
107
108def RetrieveTrybotList():
109 """ Retrieve the list of known trybots from the build master, stripping
110 TRYBOT_SUFFIX from the name. """
111 trybots = []
112 connection = httplib.HTTPConnection(SKIA_BUILD_MASTER_HOST,
113 SKIA_BUILD_MASTER_PORT)
114 connection.request('GET', '/json/builders')
115 response = connection.getresponse()
116 builders = json.load(response)
117
118 for builder in builders:
119 if builder.endswith(TRYBOT_SUFFIX):
120 trybots.append(builder[:-len(TRYBOT_SUFFIX)])
121 return trybots
122
123
124def ValidateArgs(trybots, is_svn=True):
125 """ Parse and validate command-line arguments. If the arguments are valid,
126 returns a tuple of (<changelist name>, <list of trybots>).
127
128 trybots: A list of the known try builders.
129 """
130 if is_svn:
131 parser = argparse.ArgumentParser(
132 prog=os.path.basename(__file__),
133 description='%(prog)s: Submit a try request.',
134 usage=('%(prog)s [-h] <changelist> --bot <buildername> '
135 '[<buildername ...]'))
136 parser.add_argument('changelist', metavar='<changelist>',
137 help='Changelist to try.')
138 else:
139 parser = argparse.ArgumentParser(
140 prog=os.path.basename(__file__),
141 description='%(prog)s: Submit a try request.')
142
143 parser.add_argument('-r', '--revision', metavar='<revision#>', nargs=1,
144 type=int, help='Revision from which to try the change.')
145 parser.add_argument('--bot', metavar='<buildername>', nargs='+',
146 help='Builder(s) on which to try the change. One of: %s'
147 % ', '.join(trybots),
148 choices=trybots + [ALL_BUILDERS], required=True)
149 args = parser.parse_args()
150 if args.bot == [ALL_BUILDERS]:
151 args.bot = trybots
152 return args
153
154
155def SubmitTryRequest(args, is_svn=True):
156 """ Submits a try request for the given changelist on the given list of
157 trybots.
158
159 args: Object whose properties are derived from command-line arguments. If
160 is_svn is True, it should contain:
161 - changelist: string; the name of the changelist to try.
162 - bot: list of strings; the names of the try builders to run.
163 - revision: optional, int; the revision number from which to run the try.
164 If is_svn is False, it should contain:
165 - bot: list of strings; the names of the try builders to run.
166 - revision: optional, int; the revision number from which to run the try.
167 is_svn: boolean; are we in an SVN repo?
168 """
borenet@google.com6b5388e2013-01-23 20:54:29 +0000169 botlist = ','.join(['%s%s' % (bot, TRYBOT_SUFFIX) for bot in args.bot])
170 if is_svn:
borenet@google.com6c55b512013-01-24 21:38:51 +0000171 gcl_cmd = 'gcl.bat' if os.name == 'nt' else 'gcl'
172 try_args = [gcl_cmd, 'try', args.changelist,
173 '--root', GetCheckoutRoot(is_svn),
borenet@google.com6b5388e2013-01-23 20:54:29 +0000174 '--bot', botlist]
175 if args.revision:
176 try_args.extend(['-r', args.revision])
borenet@google.com6c55b512013-01-24 21:38:51 +0000177 print ' '.join(try_args)
178 proc = subprocess.Popen(try_args, stdout=subprocess.PIPE,
179 stderr=subprocess.STDOUT)
180 if proc.wait() != 0:
181 raise Exception('Failed to submit try request: %s' % (
182 proc.communicate()[0]))
183 print proc.communicate()[0]
borenet@google.com6b5388e2013-01-23 20:54:29 +0000184 else:
borenet@google.com6c55b512013-01-24 21:38:51 +0000185 # First, find depot_tools. This is needed to import trychange.
186 sys.path.append(FindDepotTools())
borenet@google.com6b5388e2013-01-23 20:54:29 +0000187 import trychange
188 try_args = ['--use_svn',
189 '--svn_repo', GetTryRepo(),
190 '--root', GetCheckoutRoot(is_svn),
191 '--bot', botlist]
192 if args.revision:
193 try_args.extend(['-r', args.revision])
194 trychange.TryChange(try_args, None, False)
195
196
197def main():
198 # Retrieve the list of active try builders from the build master.
199 trybots = RetrieveTrybotList()
200
201 # Determine if we're in an SVN checkout.
202 is_svn = os.path.isdir('.svn')
203
204 # Parse and validate the command-line arguments.
205 args = ValidateArgs(trybots=trybots, is_svn=is_svn)
206
207 # Submit the try request.
208 SubmitTryRequest(args, is_svn=is_svn)
209
210
211if __name__ == '__main__':
212 sys.exit(main())