Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: tools/gn/bin/roll_gn.py

Issue 1230293003: Add a GN auto-roller script. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix depot_tools_path Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
brettw 2015/07/15 01:59:05 Needs copyright.
Dirk Pranke 2015/07/15 02:40:31 Acknowledged.
2
3 from __future__ import print_function
4
5 import argparse
6 import json
7 import os
8 import re
9 import subprocess
10 import sys
11 import tempfile
12 import time
13 import urllib2
14
15 depot_tools_path = None
brettw 2015/07/15 01:59:05 Can this have some documentation and an example at
Dirk Pranke 2015/07/15 02:40:32 I assume "this" means the roll_gn script as a whol
brettw 2015/07/15 02:43:37 Correct, you can be assured I did not notice the s
16 for p in os.environ['PATH'].split(os.pathsep):
17 if (p.rstrip(os.sep).endswith('depot_tools') and
18 os.path.isfile(os.path.join(p, 'gclient.py'))):
19 depot_tools_path = p
20
21 assert depot_tools_path
22
23 if not depot_tools_path in sys.path:
24 sys.path.append(depot_tools_path)
Nico 2015/07/15 20:24:25 nit: always prepend to sys.path, else you can end
Dirk Pranke 2015/07/15 20:35:56 true.
25 sys.path.append(os.path.join(depot_tools_path, 'third_party'))
26
27 import upload
28
29
30 CHROMIUM_REPO = 'https://chromium.googlesource.com/chromium/src.git'
31
32 CODE_REVIEW_SERVER = 'https://codereview.chromium.org'
33
34
35 class GNRoller(object):
36 def __init__(self):
37 self.chromium_src_dir = None
38 self.old_gn_commitish = None
39 self.new_gn_commitish = None
40 self.old_gn_version = None
41 self.new_gn_version = None
42 self.reviewer = 'dpranke@chromium.org'
43 if os.getenv('USER') == 'dpranke':
44 self.reviewer = 'brettw@chromium.org'
45
46 def Roll(self):
47 parser = argparse.ArgumentParser()
48 parser.add_argument('command', nargs='?', default='roll',
49 help='build|roll|roll_buildtools|roll_deps|wait'
50 ' (default is roll)')
51
52 args = parser.parse_args()
53 command = args.command
54 ret = self.SetUp()
55 if not ret and command in ('roll', 'build'):
56 ret = self.TriggerBuild()
57 if not ret and command in ('roll', 'wait'):
58 ret = self.WaitForBuildToFinish()
59 if not ret and command in ('roll', 'roll_buildtools'):
60 ret = self.RollBuildtools()
61 if not ret and command in ('roll', 'roll_deps'):
62 ret = self.RollDEPS()
63
64 return ret
65
66 def SetUp(self):
67 if sys.platform != 'linux2':
68 print('roll_gn is only tested and working on Linux for now.')
69 return 1
70
71 ret, out, _ = call('git config --get remote.origin.url')
72 origin = out.strip()
73 if ret or origin != CHROMIUM_REPO:
74 print('Not in a Chromium repo? git config --get remote.origin.url '
75 'returned %d: %s' % (ret, origin))
76 return 1
77
78 ret, _, _ = call('git diff -q')
79 if ret:
80 print("Checkout is dirty, exiting")
81 return 1
82
83 _, out, _ = call('git rev-parse --show-toplevel')
84 self.chromium_src_dir = out.strip()
85 os.chdir(self.chromium_src_dir)
Nico 2015/07/15 20:24:25 i always encourage people to make their code not c
Dirk Pranke 2015/07/15 20:35:56 Yeah, I do, too. In this case I was being lazy.
86
87 self.new_gn_commitish, self.new_gn_version = self.GetNewVersions()
88
89 _, out, _ = call('gn --version')
90 self.old_gn_version = out.strip()
91
92 _, out, _ = call('git crrev-parse %s' % self.old_gn_version)
93 self.old_gn_commitish = out.strip()
94 return 0
95
96 def GetNewVersions(self):
97 commit_msg = call('git log -1 --grep Cr-Commit-Position')[1].splitlines()
98 first_line = commit_msg[0]
99 new_gn_commitish = first_line.split()[1]
100
101 last_line = commit_msg[-1]
102 new_gn_version = re.sub('.*master@{#(\d+)}', '\\1', last_line)
103
104 return new_gn_commitish, new_gn_version
105
106 def TriggerBuild(self):
107 os.chdir(self.chromium_src_dir)
108 ret, _, _ = call('git new-branch build_gn_%s' % self.new_gn_version)
109 if ret:
110 print('Failed to create a new branch for build_gn_%s' %
111 self.new_gn_version)
112 return 1
113
114 self.MakeDummyDepsChange()
115
116 ret, out, err = call('git commit -a -m "Build gn at %s"' %
117 self.new_gn_version)
118 if ret:
119 print('git commit failed: %s' % out + err)
120 return 1
121
122 print('Uploading CL to build GN at {#%s} - %s' %
123 (self.new_gn_version, self.new_gn_commitish))
124 ret, out, err = call('git cl upload -f')
125 if ret:
126 print('git-cl upload failed: %s' % out + err)
127 return 1
128
129 print('Starting try jobs')
130 call('git-cl try -b linux_chromium_gn_upload -b mac_chromium_gn_upload '
131 '-b win8_chromium_gn_upload -r %s' % self.new_gn_commitish)
132
133 return 0
134
135 def MakeDummyDepsChange(self):
136 with open('DEPS') as fp:
137 deps_content = fp.read()
138 new_deps = deps_content.replace("'buildtools_revision':",
139 "'buildtools_revision': ")
140
141 with open('DEPS', 'w') as fp:
142 fp.write(new_deps)
143
144 def WaitForBuildToFinish(self):
145 print('Checking build')
146 results = self.CheckBuild()
147 while any(r['state'] == 'pending' for r in results.values()):
148 print()
149 print('Sleeping for 30 seconds')
150 time.sleep(30)
151 print('Checking build')
152 results = self.CheckBuild()
153 return 0 if all(r['state'] == 'success' for r in results.values()) else 1
154
155 def CheckBuild(self):
156 os.chdir(self.chromium_src_dir)
157 _, out, _ = call('git-cl issue')
158
159 issue = int(out.split()[2])
160
161 _, out, _ = call('git config user.email')
162 email = ''
163 rpc_server = upload.GetRpcServer(CODE_REVIEW_SERVER, email)
164 try:
165 props = json.loads(rpc_server.Send('/api/%d' % issue))
166 except Exception as _e:
167 raise
168
169 patchset = int(props['patchsets'][-1])
170
171 try:
172 patchset_data = json.loads(rpc_server.Send('/api/%d/%d' %
173 (issue, patchset)))
174 except Exception as _e:
175 raise
176
177 TRY_JOB_RESULT_STATES = ('success', 'warnings', 'failure', 'skipped',
178 'exception', 'retry', 'pending')
179 try_job_results = patchset_data['try_job_results']
180 if not try_job_results:
181 print('No try jobs found on most recent patchset')
182 return 1
183
184 results = {}
185 for job in try_job_results:
186 builder = job['builder']
187 if builder == 'linux_chromium_gn_upload':
188 platform = 'linux64'
189 elif builder == 'mac_chromium_gn_upload':
190 platform = 'mac'
191 elif builder == 'win8_chromium_gn_upload':
192 platform = 'win'
193 else:
194 print('Unexpected builder: %s')
195 continue
196
197 state = TRY_JOB_RESULT_STATES[int(job['result'])]
198 url_str = ' %s' % job['url']
199 build = url_str.split('/')[-1]
200
201 sha1 = '-'
202 results.setdefault(platform, {'build': -1, 'sha1': '', 'url': url_str})
203
204 if state == 'success':
205 jsurl = url_str.replace('/builders/', '/json/builders/')
206 fp = urllib2.urlopen(jsurl)
207 js = json.loads(fp.read())
208 fp.close()
209 for step in js['steps']:
210 if step['name'] == 'gn sha1':
211 sha1 = step['text'][1]
212
213 if results[platform]['build'] < build:
214 results[platform]['build'] = build
215 results[platform]['sha1'] = sha1
216 results[platform]['state'] = state
217 results[platform]['url'] = url_str
218
219 for platform, r in results.items():
220 print(platform)
221 print(' sha1: %s' % r['sha1'])
222 print(' state: %s' % r['state'])
223 print(' build: %s' % r['build'])
224 print(' url: %s' % r['url'])
225 print()
226
227 return results
228
229 def RollBuildtools(self):
230 os.chdir(self.chromium_src_dir)
231 results = self.CheckBuild()
232 if not all(r['state'] == 'success' for r in results.values()):
233 print("Roll isn't done or didn't succeed, exiting:")
234 return 1
235
236 desc = self.GetBuildtoolsDesc()
237
238 os.chdir('buildtools')
239 call('git new-branch roll_buildtools_gn_%s' % self.new_gn_version)
240
241 for platform in results:
242 fname = 'gn.exe.sha1' if platform == 'win' else 'gn.sha1'
Nico 2015/07/15 20:24:25 suddenly, 4-space indent?
Dirk Pranke 2015/07/15 20:35:56 bad indent. will fix.
243 with open('%s/%s' % (platform, fname), 'w') as fp:
244 fp.write('%s\n' % results[platform]['sha1'])
245
246 desc_file = tempfile.NamedTemporaryFile(delete=False)
247 try:
248 desc_file.write(desc)
249 desc_file.close()
250 call('git commit -a -F %s' % desc_file.name)
251 call('git-cl upload -f --send-mail')
252 finally:
253 os.remove(desc_file.name)
254
255 call('git cl push')
256 call('git cl fetch')
257 return 0
258
259 def RollDEPS(self):
260 os.chdir(self.chromium_src_dir)
261
262 os.chdir('buildtools')
263 _, out, _ = call('git rev-parse origin/master')
264 new_buildtools_commitish = out.strip()
265
266 new_deps_lines = []
267 os.chdir('..')
268 old_buildtools_commitish = ''
269 with open('DEPS') as fp:
270 for l in fp.readlines():
271 m = re.match(".*'buildtools_revision':.*'(.+)',", l)
272 if m:
273 old_buildtools_commitish = m.group(1)
274 new_deps_lines.append(" 'buildtools_revision': '%s'," %
275 new_buildtools_commitish)
276 else:
277 new_deps_lines.append(l)
278
279 if not old_buildtools_commitish:
280 print('Could not update DEPS properly, exiting')
281 return 1
282
283 with open('DEPS', 'w') as fp:
284 fp.write(''.join(new_deps_lines) + '\n')
285
286 desc = self.GetDEPSRollDesc(old_buildtools_commitish,
287 new_buildtools_commitish)
288 desc_file = tempfile.NamedTemporaryFile(delete=False)
289 try:
290 desc_file.write(desc)
291 desc_file.close()
292 call('git commit -a -F %s' % desc_file.name)
293 call('git-cl upload -f --send-mail --commit-queue')
294 finally:
295 os.remove(desc_file.name)
296 return 0
297
298 def GetBuildtoolsDesc(self):
299 gn_changes = self.GetGNChanges()
300 return (
301 'Roll gn %s..%s (r%s:%s)\n'
302 '\n'
303 '%s'
304 '\n'
305 'TBR=%s\n' % (
306 self.old_gn_commitish,
307 self.new_gn_commitish,
308 self.old_gn_version,
309 self.new_gn_version,
310 gn_changes,
311 self.reviewer,
312 ))
313
314 def GetDEPSRollDesc(self, old_buildtools_commitish, new_buildtools_commitish):
315 gn_changes = self.GetGNChanges()
316
317 return (
318 'Roll DEPS %s..%s\n'
319 '\n'
320 ' in order to roll GN %s..%s (r%s:%s)\n'
321 '\n'
322 '%s'
323 '\n'
324 'TBR=%s\n' % (
325 old_buildtools_commitish,
326 new_buildtools_commitish,
327 self.old_gn_commitish,
328 self.new_gn_commitish,
329 self.old_gn_version,
330 self.new_gn_version,
331 gn_changes,
332 self.reviewer,
333 ))
334
335 def GetGNChanges(self):
336 _, out, _ = call(
337 "git log --pretty=' %h %s' " +
338 "%s..%s tools/gn" % (self.old_gn_commitish, self.new_gn_commitish))
339 return out
340
341 def call(cmd):
342 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
Nico 2015/07/15 20:24:25 do you need the shell bit? if not, i'd recommend n
Dirk Pranke 2015/07/15 20:35:56 In many cases, I need shell=True. It is possible t
343 out, err = p.communicate()
344 return p.returncode, out, err
345
346
347 if __name__ == '__main__':
348 roller = GNRoller()
349 sys.exit(roller.Roll())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698