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

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: Addressed review 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
« 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
(...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
244 True 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
247 maximum number of retries is len(wait_plan).
248 """
249 retry_on = retry_on or (lambda x: False)
250 wait_plan = list(wait_plan or [])
251 wait_plan.reverse()
252 while True:
253 got_exception = False
254 try:
255 result = cb()
256 except Exception:
257 got_exception = True
258 if got_exception or retry_on(result):
259 if not wait_plan:
260 raise Exception("Retried too often. Giving up.")
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
234 def ReadLine(self, default=None): 268 def ReadLine(self, default=None):
235 # Don't prompt in forced mode. 269 # Don't prompt in forced mode.
236 if self._options and self._options.f and default is not None: 270 if self._options and self._options.f and default is not None:
237 print "%s (forced)" % default 271 print "%s (forced)" % default
238 return default 272 return default
239 else: 273 else:
240 return self._side_effect_handler.ReadLine() 274 return self._side_effect_handler.ReadLine()
241 275
242 def Git(self, args="", prefix="", pipe=True): 276 def Git(self, args="", prefix="", pipe=True, retry_on=None):
243 return self._side_effect_handler.Command("git", args, prefix, pipe) 277 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe)
278 return self.Retry(cmd, retry_on, [5, 30])
244 279
245 def Editor(self, args): 280 def Editor(self, args):
246 return self._side_effect_handler.Command(os.environ["EDITOR"], args, 281 return self._side_effect_handler.Command(os.environ["EDITOR"], args,
247 pipe=False) 282 pipe=False)
248 283
249 def ReadURL(self, url): 284 def ReadURL(self, url, retry_on=None, wait_plan=None):
250 return self._side_effect_handler.ReadURL(url) 285 wait_plan = wait_plan or [3, 60, 600]
286 cmd = lambda: self._side_effect_handler.ReadURL(url)
287 return self.Retry(cmd, retry_on, wait_plan)
251 288
252 def Die(self, msg=""): 289 def Die(self, msg=""):
253 if msg != "": 290 if msg != "":
254 print "Error: %s" % msg 291 print "Error: %s" % msg
255 print "Exiting" 292 print "Exiting"
256 raise Exception(msg) 293 raise Exception(msg)
257 294
258 def DieInForcedMode(self, msg=""): 295 def DieInForcedMode(self, msg=""):
259 if self._options and self._options.f: 296 if self._options and self._options.f:
260 msg = msg or "Not implemented in forced mode." 297 msg = msg or "Not implemented in forced mode."
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 options, 477 options,
441 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): 478 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
442 state = {} 479 state = {}
443 steps = [] 480 steps = []
444 for (number, step_class) in enumerate(step_classes): 481 for (number, step_class) in enumerate(step_classes):
445 steps.append(MakeStep(step_class, number, state, config, 482 steps.append(MakeStep(step_class, number, state, config,
446 options, side_effect_handler)) 483 options, side_effect_handler))
447 484
448 for step in steps[options.s:]: 485 for step in steps[options.s:]:
449 step.Run() 486 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