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

Side by Side Diff: tools/push-to-trunk/common_includes.py

Issue 136643008: A64: Synchronize with r18256. (Closed) Base URL: https://v8.googlecode.com/svn/branches/experimental/a64
Patch Set: Created 6 years, 10 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 | Annotate | Revision Log
« no previous file with comments | « tools/push-to-trunk/auto_roll.py ('k') | tools/push-to-trunk/push_to_trunk.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2013 the V8 project authors. All rights reserved. 2 # Copyright 2013 the V8 project authors. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following 10 # copyright notice, this list of conditions and the following
11 # disclaimer in the documentation and/or other materials provided 11 # disclaimer in the documentation and/or other materials provided
12 # with the distribution. 12 # with the distribution.
13 # * Neither the name of Google Inc. nor the names of its 13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived 14 # contributors may be used to endorse or promote products derived
15 # from this software without specific prior written permission. 15 # from this software without specific prior written permission.
16 # 16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 import datetime
29 import os 30 import os
30 import re 31 import re
31 import subprocess 32 import subprocess
32 import sys 33 import sys
33 import textwrap 34 import textwrap
35 import time
34 import urllib2 36 import urllib2
35 37
36 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" 38 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME"
37 TEMP_BRANCH = "TEMP_BRANCH" 39 TEMP_BRANCH = "TEMP_BRANCH"
38 BRANCHNAME = "BRANCHNAME" 40 BRANCHNAME = "BRANCHNAME"
39 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" 41 DOT_GIT_LOCATION = "DOT_GIT_LOCATION"
40 VERSION_FILE = "VERSION_FILE" 42 VERSION_FILE = "VERSION_FILE"
41 CHANGELOG_FILE = "CHANGELOG_FILE" 43 CHANGELOG_FILE = "CHANGELOG_FILE"
42 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" 44 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE"
43 COMMITMSG_FILE = "COMMITMSG_FILE" 45 COMMITMSG_FILE = "COMMITMSG_FILE"
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 99
98 def MakeChangeLogBody(commit_messages, auto_format=False): 100 def MakeChangeLogBody(commit_messages, auto_format=False):
99 result = "" 101 result = ""
100 added_titles = set() 102 added_titles = set()
101 for (title, body, author) in commit_messages: 103 for (title, body, author) in commit_messages:
102 # TODO(machenbach): Better check for reverts. A revert should remove the 104 # TODO(machenbach): Better check for reverts. A revert should remove the
103 # original CL from the actual log entry. 105 # original CL from the actual log entry.
104 title = title.strip() 106 title = title.strip()
105 if auto_format: 107 if auto_format:
106 # Only add commits that set the LOG flag correctly. 108 # Only add commits that set the LOG flag correctly.
107 log_exp = r"^[ \t]*LOG[ \t]*=[ \t]*(?:Y(?:ES)?)|TRUE" 109 log_exp = r"^[ \t]*LOG[ \t]*=[ \t]*(?:(?:Y(?:ES)?)|TRUE)"
108 if not re.search(log_exp, body, flags=re.I | re.M): 110 if not re.search(log_exp, body, flags=re.I | re.M):
109 continue 111 continue
110 # Never include reverts. 112 # Never include reverts.
111 if title.startswith("Revert "): 113 if title.startswith("Revert "):
112 continue 114 continue
113 # Don't include duplicates. 115 # Don't include duplicates.
114 if title in added_titles: 116 if title in added_titles:
115 continue 117 continue
116 118
117 # Add and format the commit's title and bug reference. Move dot to the end. 119 # Add and format the commit's title and bug reference. Move dot to the end.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 168
167 if len(bug_groups) > 0: 169 if len(bug_groups) > 0:
168 return "(%s)" % ", ".join(bug_groups) 170 return "(%s)" % ", ".join(bug_groups)
169 else: 171 else:
170 return "" 172 return ""
171 173
172 174
173 # Some commands don't like the pipe, e.g. calling vi from within the script or 175 # Some commands don't like the pipe, e.g. calling vi from within the script or
174 # from subscripts like git cl upload. 176 # from subscripts like git cl upload.
175 def Command(cmd, args="", prefix="", pipe=True): 177 def Command(cmd, args="", prefix="", pipe=True):
178 # TODO(machenbach): Use timeout.
176 cmd_line = "%s %s %s" % (prefix, cmd, args) 179 cmd_line = "%s %s %s" % (prefix, cmd, args)
177 print "Command: %s" % cmd_line 180 print "Command: %s" % cmd_line
178 try: 181 try:
179 if pipe: 182 if pipe:
180 return subprocess.check_output(cmd_line, shell=True) 183 return subprocess.check_output(cmd_line, shell=True)
181 else: 184 else:
182 return subprocess.check_call(cmd_line, shell=True) 185 return subprocess.check_call(cmd_line, shell=True)
183 except subprocess.CalledProcessError: 186 except subprocess.CalledProcessError:
184 return None 187 return None
185 188
186 189
187 # Wrapper for side effects. 190 # Wrapper for side effects.
188 class SideEffectHandler(object): 191 class SideEffectHandler(object):
189 def Command(self, cmd, args="", prefix="", pipe=True): 192 def Command(self, cmd, args="", prefix="", pipe=True):
190 return Command(cmd, args, prefix, pipe) 193 return Command(cmd, args, prefix, pipe)
191 194
192 def ReadLine(self): 195 def ReadLine(self):
193 return sys.stdin.readline().strip() 196 return sys.stdin.readline().strip()
194 197
195 def ReadURL(self, url): 198 def ReadURL(self, url):
196 # pylint: disable=E1121 199 # pylint: disable=E1121
197 url_fh = urllib2.urlopen(url, None, 60) 200 url_fh = urllib2.urlopen(url, None, 60)
198 try: 201 try:
199 return url_fh.read() 202 return url_fh.read()
200 finally: 203 finally:
201 url_fh.close() 204 url_fh.close()
202 205
206 def Sleep(self, seconds):
207 time.sleep(seconds)
208
209 def GetDate(self):
210 return datetime.date.today().strftime("%Y-%m-%d")
211
203 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() 212 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler()
204 213
205 214
206 class Step(object): 215 class Step(object):
207 def __init__(self, text, requires, number, config, state, options, handler): 216 def __init__(self, text, requires, number, config, state, options, handler):
208 self._text = text 217 self._text = text
209 self._requires = requires 218 self._requires = requires
210 self._number = number 219 self._number = number
211 self._config = config 220 self._config = config
212 self._state = state 221 self._state = state
213 self._options = options 222 self._options = options
214 self._side_effect_handler = handler 223 self._side_effect_handler = handler
215 assert self._number >= 0 224 assert self._number >= 0
216 assert self._config is not None 225 assert self._config is not None
217 assert self._state is not None 226 assert self._state is not None
218 assert self._side_effect_handler is not None 227 assert self._side_effect_handler is not None
219 228
220 def Config(self, key): 229 def Config(self, key):
221 return self._config[key] 230 return self._config[key]
222 231
232 def IsForced(self):
233 return self._options and self._options.f
234
235 def IsManual(self):
236 return self._options and self._options.m
237
223 def Run(self): 238 def Run(self):
224 if self._requires: 239 if self._requires:
225 self.RestoreIfUnset(self._requires) 240 self.RestoreIfUnset(self._requires)
226 if not self._state[self._requires]: 241 if not self._state[self._requires]:
227 return 242 return
228 print ">>> Step %d: %s" % (self._number, self._text) 243 print ">>> Step %d: %s" % (self._number, self._text)
229 self.RunStep() 244 self.RunStep()
230 245
231 def RunStep(self): 246 def RunStep(self):
232 raise NotImplementedError 247 raise NotImplementedError
233 248
249 def Retry(self, cb, retry_on=None, wait_plan=None):
250 """ Retry a function.
251 Params:
252 cb: The function to retry.
253 retry_on: A callback that takes the result of the function and returns
254 True if the function should be retried. A function throwing an
255 exception is always retried.
256 wait_plan: A list of waiting delays between retries in seconds. The
257 maximum number of retries is len(wait_plan).
258 """
259 retry_on = retry_on or (lambda x: False)
260 wait_plan = list(wait_plan or [])
261 wait_plan.reverse()
262 while True:
263 got_exception = False
264 try:
265 result = cb()
266 except Exception:
267 got_exception = True
268 if got_exception or retry_on(result):
269 if not wait_plan:
270 raise Exception("Retried too often. Giving up.")
271 wait_time = wait_plan.pop()
272 print "Waiting for %f seconds." % wait_time
273 self._side_effect_handler.Sleep(wait_time)
274 print "Retrying..."
275 else:
276 return result
277
234 def ReadLine(self, default=None): 278 def ReadLine(self, default=None):
235 # Don't prompt in forced mode. 279 # Don't prompt in forced mode.
236 if self._options and self._options.f and default is not None: 280 if not self.IsManual() and default is not None:
237 print "%s (forced)" % default 281 print "%s (forced)" % default
238 return default 282 return default
239 else: 283 else:
240 return self._side_effect_handler.ReadLine() 284 return self._side_effect_handler.ReadLine()
241 285
242 def Git(self, args="", prefix="", pipe=True): 286 def Git(self, args="", prefix="", pipe=True, retry_on=None):
243 return self._side_effect_handler.Command("git", args, prefix, pipe) 287 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe)
288 return self.Retry(cmd, retry_on, [5, 30])
244 289
245 def Editor(self, args): 290 def Editor(self, args):
246 return self._side_effect_handler.Command(os.environ["EDITOR"], args, 291 if not self.IsForced():
247 pipe=False) 292 return self._side_effect_handler.Command(os.environ["EDITOR"], args,
293 pipe=False)
248 294
249 def ReadURL(self, url): 295 def ReadURL(self, url, retry_on=None, wait_plan=None):
250 return self._side_effect_handler.ReadURL(url) 296 wait_plan = wait_plan or [3, 60, 600]
297 cmd = lambda: self._side_effect_handler.ReadURL(url)
298 return self.Retry(cmd, retry_on, wait_plan)
299
300 def GetDate(self):
301 return self._side_effect_handler.GetDate()
251 302
252 def Die(self, msg=""): 303 def Die(self, msg=""):
253 if msg != "": 304 if msg != "":
254 print "Error: %s" % msg 305 print "Error: %s" % msg
255 print "Exiting" 306 print "Exiting"
256 raise Exception(msg) 307 raise Exception(msg)
257 308
258 def DieInForcedMode(self, msg=""): 309 def DieNoManualMode(self, msg=""):
259 if self._options and self._options.f: 310 if not self.IsManual():
260 msg = msg or "Not implemented in forced mode." 311 msg = msg or "Only available in manual mode."
261 self.Die(msg) 312 self.Die(msg)
262 313
263 def Confirm(self, msg): 314 def Confirm(self, msg):
264 print "%s [Y/n] " % msg, 315 print "%s [Y/n] " % msg,
265 answer = self.ReadLine(default="Y") 316 answer = self.ReadLine(default="Y")
266 return answer == "" or answer == "Y" or answer == "y" 317 return answer == "" or answer == "Y" or answer == "y"
267 318
268 def DeleteBranch(self, name): 319 def DeleteBranch(self, name):
269 git_result = self.Git("branch").strip() 320 git_result = self.Git("branch").strip()
270 for line in git_result.splitlines(): 321 for line in git_result.splitlines():
(...skipping 18 matching lines...) Expand all
289 340
290 def RestoreIfUnset(self, var_name): 341 def RestoreIfUnset(self, var_name):
291 if self._state.get(var_name) is None: 342 if self._state.get(var_name) is None:
292 self._state[var_name] = self.Restore(var_name) 343 self._state[var_name] = self.Restore(var_name)
293 344
294 def InitialEnvironmentChecks(self): 345 def InitialEnvironmentChecks(self):
295 # Cancel if this is not a git checkout. 346 # Cancel if this is not a git checkout.
296 if not os.path.exists(self._config[DOT_GIT_LOCATION]): 347 if not os.path.exists(self._config[DOT_GIT_LOCATION]):
297 self.Die("This is not a git checkout, this script won't work for you.") 348 self.Die("This is not a git checkout, this script won't work for you.")
298 349
299 # TODO(machenbach): Don't use EDITOR in forced mode as soon as script is
300 # well tested.
301 # Cancel if EDITOR is unset or not executable. 350 # Cancel if EDITOR is unset or not executable.
302 if (not os.environ.get("EDITOR") or 351 if (not self.IsForced() and (not os.environ.get("EDITOR") or
303 Command("which", os.environ["EDITOR"]) is None): 352 Command("which", os.environ["EDITOR"]) is None)):
304 self.Die("Please set your EDITOR environment variable, you'll need it.") 353 self.Die("Please set your EDITOR environment variable, you'll need it.")
305 354
306 def CommonPrepare(self): 355 def CommonPrepare(self):
307 # Check for a clean workdir. 356 # Check for a clean workdir.
308 if self.Git("status -s -uno").strip() != "": 357 if self.Git("status -s -uno").strip() != "":
309 self.Die("Workspace is not clean. Please commit or undo your changes.") 358 self.Die("Workspace is not clean. Please commit or undo your changes.")
310 359
311 # Persist current branch. 360 # Persist current branch.
312 current_branch = "" 361 current_branch = ""
313 git_result = self.Git("status -s -b -uno").strip() 362 git_result = self.Git("status -s -b -uno").strip()
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 self.RestoreIfUnset("%s%s" % (prefix, v)) 411 self.RestoreIfUnset("%s%s" % (prefix, v))
363 412
364 def WaitForLGTM(self): 413 def WaitForLGTM(self):
365 print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit " 414 print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit "
366 "your change. (If you need to iterate on the patch or double check " 415 "your change. (If you need to iterate on the patch or double check "
367 "that it's sane, do so in another shell, but remember to not " 416 "that it's sane, do so in another shell, but remember to not "
368 "change the headline of the uploaded CL.") 417 "change the headline of the uploaded CL.")
369 answer = "" 418 answer = ""
370 while answer != "LGTM": 419 while answer != "LGTM":
371 print "> ", 420 print "> ",
372 # TODO(machenbach): Add default="LGTM" to avoid prompt when script is 421 answer = self.ReadLine("LGTM" if self.IsForced() else None)
373 # well tested and when prepare push cl has TBR flag.
374 answer = self.ReadLine()
375 if answer != "LGTM": 422 if answer != "LGTM":
376 print "That was not 'LGTM'." 423 print "That was not 'LGTM'."
377 424
378 def WaitForResolvingConflicts(self, patch_file): 425 def WaitForResolvingConflicts(self, patch_file):
379 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", " 426 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", "
380 "or resolve the conflicts, stage *all* touched files with " 427 "or resolve the conflicts, stage *all* touched files with "
381 "'git add', and type \"RESOLVED<Return>\"") 428 "'git add', and type \"RESOLVED<Return>\"")
382 self.DieInForcedMode() 429 self.DieNoManualMode()
383 answer = "" 430 answer = ""
384 while answer != "RESOLVED": 431 while answer != "RESOLVED":
385 if answer == "ABORT": 432 if answer == "ABORT":
386 self.Die("Applying the patch failed.") 433 self.Die("Applying the patch failed.")
387 if answer != "": 434 if answer != "":
388 print "That was not 'RESOLVED' or 'ABORT'." 435 print "That was not 'RESOLVED' or 'ABORT'."
389 print "> ", 436 print "> ",
390 answer = self.ReadLine() 437 answer = self.ReadLine()
391 438
392 # Takes a file containing the patch to apply as first argument. 439 # Takes a file containing the patch to apply as first argument.
393 def ApplyPatch(self, patch_file, reverse_patch=""): 440 def ApplyPatch(self, patch_file, reverse_patch=""):
394 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) 441 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file)
395 if self.Git(args) is None: 442 if self.Git(args) is None:
396 self.WaitForResolvingConflicts(patch_file) 443 self.WaitForResolvingConflicts(patch_file)
397 444
398 445
399 class UploadStep(Step): 446 class UploadStep(Step):
400 MESSAGE = "Upload for code review." 447 MESSAGE = "Upload for code review."
401 448
402 def RunStep(self): 449 def RunStep(self):
403 if self._options.r: 450 if self._options.r:
404 print "Using account %s for review." % self._options.r 451 print "Using account %s for review." % self._options.r
405 reviewer = self._options.r 452 reviewer = self._options.r
406 else: 453 else:
407 print "Please enter the email address of a V8 reviewer for your patch: ", 454 print "Please enter the email address of a V8 reviewer for your patch: ",
408 self.DieInForcedMode("A reviewer must be specified in forced mode.") 455 self.DieNoManualMode("A reviewer must be specified in forced mode.")
409 reviewer = self.ReadLine() 456 reviewer = self.ReadLine()
410 force_flag = " -f" if self._options.f else "" 457 force_flag = " -f" if not self.IsManual() else ""
411 args = "cl upload -r \"%s\" --send-mail%s" % (reviewer, force_flag) 458 args = "cl upload -r \"%s\" --send-mail%s" % (reviewer, force_flag)
412 # TODO(machenbach): Check output in forced mode. Verify that all required 459 # TODO(machenbach): Check output in forced mode. Verify that all required
413 # base files were uploaded, if not retry. 460 # base files were uploaded, if not retry.
414 if self.Git(args, pipe=False) is None: 461 if self.Git(args, pipe=False) is None:
415 self.Die("'git cl upload' failed, please try again.") 462 self.Die("'git cl upload' failed, please try again.")
416 463
417 464
418 def MakeStep(step_class=Step, number=0, state=None, config=None, 465 def MakeStep(step_class=Step, number=0, state=None, config=None,
419 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): 466 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
420 # Allow to pass in empty dictionaries. 467 # Allow to pass in empty dictionaries.
(...skipping 19 matching lines...) Expand all
440 options, 487 options,
441 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): 488 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
442 state = {} 489 state = {}
443 steps = [] 490 steps = []
444 for (number, step_class) in enumerate(step_classes): 491 for (number, step_class) in enumerate(step_classes):
445 steps.append(MakeStep(step_class, number, state, config, 492 steps.append(MakeStep(step_class, number, state, config,
446 options, side_effect_handler)) 493 options, side_effect_handler))
447 494
448 for step in steps[options.s:]: 495 for step in steps[options.s:]:
449 step.Run() 496 step.Run()
OLDNEW
« no previous file with comments | « tools/push-to-trunk/auto_roll.py ('k') | tools/push-to-trunk/push_to_trunk.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698