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

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

Issue 91733003: Add retry feature for push-to-trunk script. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Added comments. Created 7 years 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
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
(...skipping 13 matching lines...) Expand all
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 os 29 import os
30 import re 30 import re
31 import subprocess 31 import subprocess
32 import sys 32 import sys
33 import textwrap 33 import textwrap
34 import time
34 import urllib2 35 import urllib2
35 36
36 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" 37 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME"
37 TEMP_BRANCH = "TEMP_BRANCH" 38 TEMP_BRANCH = "TEMP_BRANCH"
38 BRANCHNAME = "BRANCHNAME" 39 BRANCHNAME = "BRANCHNAME"
39 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" 40 DOT_GIT_LOCATION = "DOT_GIT_LOCATION"
40 VERSION_FILE = "VERSION_FILE" 41 VERSION_FILE = "VERSION_FILE"
41 CHANGELOG_FILE = "CHANGELOG_FILE" 42 CHANGELOG_FILE = "CHANGELOG_FILE"
42 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" 43 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE"
43 COMMITMSG_FILE = "COMMITMSG_FILE" 44 COMMITMSG_FILE = "COMMITMSG_FILE"
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 167
167 if len(bug_groups) > 0: 168 if len(bug_groups) > 0:
168 return "(%s)" % ", ".join(bug_groups) 169 return "(%s)" % ", ".join(bug_groups)
169 else: 170 else:
170 return "" 171 return ""
171 172
172 173
173 # Some commands don't like the pipe, e.g. calling vi from within the script or 174 # Some commands don't like the pipe, e.g. calling vi from within the script or
174 # from subscripts like git cl upload. 175 # from subscripts like git cl upload.
175 def Command(cmd, args="", prefix="", pipe=True): 176 def Command(cmd, args="", prefix="", pipe=True):
177 # TODO(machenbach): Use timeout.
176 cmd_line = "%s %s %s" % (prefix, cmd, args) 178 cmd_line = "%s %s %s" % (prefix, cmd, args)
177 print "Command: %s" % cmd_line 179 print "Command: %s" % cmd_line
178 try: 180 try:
179 if pipe: 181 if pipe:
180 return subprocess.check_output(cmd_line, shell=True) 182 return subprocess.check_output(cmd_line, shell=True)
181 else: 183 else:
182 return subprocess.check_call(cmd_line, shell=True) 184 return subprocess.check_call(cmd_line, shell=True)
183 except subprocess.CalledProcessError: 185 except subprocess.CalledProcessError:
184 return None 186 return None
185 187
186 188
187 # Wrapper for side effects. 189 # Wrapper for side effects.
188 class SideEffectHandler(object): 190 class SideEffectHandler(object):
189 def Command(self, cmd, args="", prefix="", pipe=True): 191 def Command(self, cmd, args="", prefix="", pipe=True):
190 return Command(cmd, args, prefix, pipe) 192 return Command(cmd, args, prefix, pipe)
191 193
192 def ReadLine(self): 194 def ReadLine(self):
193 return sys.stdin.readline().strip() 195 return sys.stdin.readline().strip()
194 196
195 def ReadURL(self, url): 197 def ReadURL(self, url):
196 # pylint: disable=E1121 198 # pylint: disable=E1121
197 url_fh = urllib2.urlopen(url, None, 60) 199 url_fh = urllib2.urlopen(url, None, 60)
198 try: 200 try:
199 return url_fh.read() 201 return url_fh.read()
200 finally: 202 finally:
201 url_fh.close() 203 url_fh.close()
202 204
205 def Sleep(seconds):
206 time.sleep(seconds)
207
203 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() 208 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler()
204 209
205 210
206 class Step(object): 211 class Step(object):
207 def __init__(self, text, requires, number, config, state, options, handler): 212 def __init__(self, text, requires, number, config, state, options, handler):
208 self._text = text 213 self._text = text
209 self._requires = requires 214 self._requires = requires
210 self._number = number 215 self._number = number
211 self._config = config 216 self._config = config
212 self._state = state 217 self._state = state
(...skipping 11 matching lines...) Expand all
224 if self._requires: 229 if self._requires:
225 self.RestoreIfUnset(self._requires) 230 self.RestoreIfUnset(self._requires)
226 if not self._state[self._requires]: 231 if not self._state[self._requires]:
227 return 232 return
228 print ">>> Step %d: %s" % (self._number, self._text) 233 print ">>> Step %d: %s" % (self._number, self._text)
229 self.RunStep() 234 self.RunStep()
230 235
231 def RunStep(self): 236 def RunStep(self):
232 raise NotImplementedError 237 raise NotImplementedError
233 238
239 def Retry(self, cb, retry_on=None, wait_plan=None):
240 """ Retry a function.
241 Params:
242 cb: The function to retry.
243 retry_on: A callback that takes the result of the function and returns Tru e
Jakob Kummerow 2013/11/28 13:47:27 nit: 80col
Michael Achenbach 2013/11/28 14:28:17 Done.
244 if the function should be retried. A function throwing an
245 exception is always retried.
246 wait_plan: A list of waiting delays between retries in seconds. The maximu m
Jakob Kummerow 2013/11/28 13:47:27 nit: 80col
Michael Achenbach 2013/11/28 14:28:17 Done.
247 number of retries is len(wait_plan).
248 """
249 retry_on = retry_on or (lambda x: False)
250 wait_plan = list(wait_plan or [])
Jakob Kummerow 2013/11/28 13:47:27 I don't think the explicit list() conversion is ne
Michael Achenbach 2013/11/28 14:28:17 I'd rather keep it, since the method has side effe
251 # Sentinel for the first try.
Jakob Kummerow 2013/11/28 13:47:27 s/first/last/! This is seriously misleading. Here
Michael Achenbach 2013/11/28 14:28:17 I take it :) Just added an initialization of got_e
252 wait_plan.append(0)
253 wait_plan.reverse()
254 while wait_plan:
255 got_exception = False
256 try:
257 result = cb()
258 except Exception:
259 got_exception = True
260 if got_exception or retry_on(result):
261 wait_time = wait_plan.pop()
262 print "Waiting for %f seconds." % wait_time
263 self._side_effect_handler.Sleep(wait_time)
264 print "Retrying..."
265 else:
266 return result
267 raise Exception("Retried too often. Giving up.")
268
234 def ReadLine(self, default=None): 269 def ReadLine(self, default=None):
235 # Don't prompt in forced mode. 270 # Don't prompt in forced mode.
236 if self._options and self._options.f and default is not None: 271 if self._options and self._options.f and default is not None:
237 print "%s (forced)" % default 272 print "%s (forced)" % default
238 return default 273 return default
239 else: 274 else:
240 return self._side_effect_handler.ReadLine() 275 return self._side_effect_handler.ReadLine()
241 276
242 def Git(self, args="", prefix="", pipe=True): 277 def Git(self, args="", prefix="", pipe=True, retry_on=None):
243 return self._side_effect_handler.Command("git", args, prefix, pipe) 278 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe)
279 return self.Retry(cmd, retry_on, [5, 30])
244 280
245 def Editor(self, args): 281 def Editor(self, args):
246 return self._side_effect_handler.Command(os.environ["EDITOR"], args, 282 return self._side_effect_handler.Command(os.environ["EDITOR"], args,
247 pipe=False) 283 pipe=False)
248 284
249 def ReadURL(self, url): 285 def ReadURL(self, url, retry_on=None, wait_plan=None):
250 return self._side_effect_handler.ReadURL(url) 286 wait_plan = wait_plan or [3, 10, 30]
Jakob Kummerow 2013/11/28 13:47:27 I'd give the server much more time. Maybe [3, 60,
Michael Achenbach 2013/11/28 14:28:17 Done.
287 cmd = lambda: self._side_effect_handler.ReadURL(url)
288 return self.Retry(cmd, retry_on, wait_plan)
251 289
252 def Die(self, msg=""): 290 def Die(self, msg=""):
253 if msg != "": 291 if msg != "":
254 print "Error: %s" % msg 292 print "Error: %s" % msg
255 print "Exiting" 293 print "Exiting"
256 raise Exception(msg) 294 raise Exception(msg)
257 295
258 def DieInForcedMode(self, msg=""): 296 def DieInForcedMode(self, msg=""):
259 if self._options and self._options.f: 297 if self._options and self._options.f:
260 msg = msg or "Not implemented in forced mode." 298 msg = msg or "Not implemented in forced mode."
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 options, 478 options,
441 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): 479 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
442 state = {} 480 state = {}
443 steps = [] 481 steps = []
444 for (number, step_class) in enumerate(step_classes): 482 for (number, step_class) in enumerate(step_classes):
445 steps.append(MakeStep(step_class, number, state, config, 483 steps.append(MakeStep(step_class, number, state, config,
446 options, side_effect_handler)) 484 options, side_effect_handler))
447 485
448 for step in steps[options.s:]: 486 for step in steps[options.s:]:
449 step.Run() 487 step.Run()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698