OLD | NEW |
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 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
222 self._options = options | 222 self._options = options |
223 self._side_effect_handler = handler | 223 self._side_effect_handler = handler |
224 assert self._number >= 0 | 224 assert self._number >= 0 |
225 assert self._config is not None | 225 assert self._config is not None |
226 assert self._state is not None | 226 assert self._state is not None |
227 assert self._side_effect_handler is not None | 227 assert self._side_effect_handler is not None |
228 | 228 |
229 def Config(self, key): | 229 def Config(self, key): |
230 return self._config[key] | 230 return self._config[key] |
231 | 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 |
232 def Run(self): | 238 def Run(self): |
233 if self._requires: | 239 if self._requires: |
234 self.RestoreIfUnset(self._requires) | 240 self.RestoreIfUnset(self._requires) |
235 if not self._state[self._requires]: | 241 if not self._state[self._requires]: |
236 return | 242 return |
237 print ">>> Step %d: %s" % (self._number, self._text) | 243 print ">>> Step %d: %s" % (self._number, self._text) |
238 self.RunStep() | 244 self.RunStep() |
239 | 245 |
240 def RunStep(self): | 246 def RunStep(self): |
241 raise NotImplementedError | 247 raise NotImplementedError |
(...skipping 22 matching lines...) Expand all Loading... |
264 raise Exception("Retried too often. Giving up.") | 270 raise Exception("Retried too often. Giving up.") |
265 wait_time = wait_plan.pop() | 271 wait_time = wait_plan.pop() |
266 print "Waiting for %f seconds." % wait_time | 272 print "Waiting for %f seconds." % wait_time |
267 self._side_effect_handler.Sleep(wait_time) | 273 self._side_effect_handler.Sleep(wait_time) |
268 print "Retrying..." | 274 print "Retrying..." |
269 else: | 275 else: |
270 return result | 276 return result |
271 | 277 |
272 def ReadLine(self, default=None): | 278 def ReadLine(self, default=None): |
273 # Don't prompt in forced mode. | 279 # Don't prompt in forced mode. |
274 if self._options and self._options.f and default is not None: | 280 if not self.IsManual() and default is not None: |
275 print "%s (forced)" % default | 281 print "%s (forced)" % default |
276 return default | 282 return default |
277 else: | 283 else: |
278 return self._side_effect_handler.ReadLine() | 284 return self._side_effect_handler.ReadLine() |
279 | 285 |
280 def Git(self, args="", prefix="", pipe=True, retry_on=None): | 286 def Git(self, args="", prefix="", pipe=True, retry_on=None): |
281 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe) | 287 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe) |
282 return self.Retry(cmd, retry_on, [5, 30]) | 288 return self.Retry(cmd, retry_on, [5, 30]) |
283 | 289 |
284 def Editor(self, args): | 290 def Editor(self, args): |
285 return self._side_effect_handler.Command(os.environ["EDITOR"], args, | 291 if not self.IsForced(): |
286 pipe=False) | 292 return self._side_effect_handler.Command(os.environ["EDITOR"], args, |
| 293 pipe=False) |
287 | 294 |
288 def ReadURL(self, url, retry_on=None, wait_plan=None): | 295 def ReadURL(self, url, retry_on=None, wait_plan=None): |
289 wait_plan = wait_plan or [3, 60, 600] | 296 wait_plan = wait_plan or [3, 60, 600] |
290 cmd = lambda: self._side_effect_handler.ReadURL(url) | 297 cmd = lambda: self._side_effect_handler.ReadURL(url) |
291 return self.Retry(cmd, retry_on, wait_plan) | 298 return self.Retry(cmd, retry_on, wait_plan) |
292 | 299 |
293 def GetDate(self): | 300 def GetDate(self): |
294 return self._side_effect_handler.GetDate() | 301 return self._side_effect_handler.GetDate() |
295 | 302 |
296 def Die(self, msg=""): | 303 def Die(self, msg=""): |
297 if msg != "": | 304 if msg != "": |
298 print "Error: %s" % msg | 305 print "Error: %s" % msg |
299 print "Exiting" | 306 print "Exiting" |
300 raise Exception(msg) | 307 raise Exception(msg) |
301 | 308 |
302 def DieInForcedMode(self, msg=""): | 309 def DieNoManualMode(self, msg=""): |
303 if self._options and self._options.f: | 310 if not self.IsManual(): |
304 msg = msg or "Not implemented in forced mode." | 311 msg = msg or "Only available in manual mode." |
305 self.Die(msg) | 312 self.Die(msg) |
306 | 313 |
307 def Confirm(self, msg): | 314 def Confirm(self, msg): |
308 print "%s [Y/n] " % msg, | 315 print "%s [Y/n] " % msg, |
309 answer = self.ReadLine(default="Y") | 316 answer = self.ReadLine(default="Y") |
310 return answer == "" or answer == "Y" or answer == "y" | 317 return answer == "" or answer == "Y" or answer == "y" |
311 | 318 |
312 def DeleteBranch(self, name): | 319 def DeleteBranch(self, name): |
313 git_result = self.Git("branch").strip() | 320 git_result = self.Git("branch").strip() |
314 for line in git_result.splitlines(): | 321 for line in git_result.splitlines(): |
(...skipping 18 matching lines...) Expand all Loading... |
333 | 340 |
334 def RestoreIfUnset(self, var_name): | 341 def RestoreIfUnset(self, var_name): |
335 if self._state.get(var_name) is None: | 342 if self._state.get(var_name) is None: |
336 self._state[var_name] = self.Restore(var_name) | 343 self._state[var_name] = self.Restore(var_name) |
337 | 344 |
338 def InitialEnvironmentChecks(self): | 345 def InitialEnvironmentChecks(self): |
339 # Cancel if this is not a git checkout. | 346 # Cancel if this is not a git checkout. |
340 if not os.path.exists(self._config[DOT_GIT_LOCATION]): | 347 if not os.path.exists(self._config[DOT_GIT_LOCATION]): |
341 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.") |
342 | 349 |
343 # TODO(machenbach): Don't use EDITOR in forced mode as soon as script is | |
344 # well tested. | |
345 # Cancel if EDITOR is unset or not executable. | 350 # Cancel if EDITOR is unset or not executable. |
346 if (not os.environ.get("EDITOR") or | 351 if (not self.IsForced() and (not os.environ.get("EDITOR") or |
347 Command("which", os.environ["EDITOR"]) is None): | 352 Command("which", os.environ["EDITOR"]) is None)): |
348 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.") |
349 | 354 |
350 def CommonPrepare(self): | 355 def CommonPrepare(self): |
351 # Check for a clean workdir. | 356 # Check for a clean workdir. |
352 if self.Git("status -s -uno").strip() != "": | 357 if self.Git("status -s -uno").strip() != "": |
353 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.") |
354 | 359 |
355 # Persist current branch. | 360 # Persist current branch. |
356 current_branch = "" | 361 current_branch = "" |
357 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 Loading... |
406 self.RestoreIfUnset("%s%s" % (prefix, v)) | 411 self.RestoreIfUnset("%s%s" % (prefix, v)) |
407 | 412 |
408 def WaitForLGTM(self): | 413 def WaitForLGTM(self): |
409 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 " |
410 "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 " |
411 "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 " |
412 "change the headline of the uploaded CL.") | 417 "change the headline of the uploaded CL.") |
413 answer = "" | 418 answer = "" |
414 while answer != "LGTM": | 419 while answer != "LGTM": |
415 print "> ", | 420 print "> ", |
416 # TODO(machenbach): Add default="LGTM" to avoid prompt when script is | 421 answer = self.ReadLine("LGTM" if self.IsForced() else None) |
417 # well tested and when prepare push cl has TBR flag. | |
418 answer = self.ReadLine() | |
419 if answer != "LGTM": | 422 if answer != "LGTM": |
420 print "That was not 'LGTM'." | 423 print "That was not 'LGTM'." |
421 | 424 |
422 def WaitForResolvingConflicts(self, patch_file): | 425 def WaitForResolvingConflicts(self, patch_file): |
423 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", " | 426 print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", " |
424 "or resolve the conflicts, stage *all* touched files with " | 427 "or resolve the conflicts, stage *all* touched files with " |
425 "'git add', and type \"RESOLVED<Return>\"") | 428 "'git add', and type \"RESOLVED<Return>\"") |
426 self.DieInForcedMode() | 429 self.DieNoManualMode() |
427 answer = "" | 430 answer = "" |
428 while answer != "RESOLVED": | 431 while answer != "RESOLVED": |
429 if answer == "ABORT": | 432 if answer == "ABORT": |
430 self.Die("Applying the patch failed.") | 433 self.Die("Applying the patch failed.") |
431 if answer != "": | 434 if answer != "": |
432 print "That was not 'RESOLVED' or 'ABORT'." | 435 print "That was not 'RESOLVED' or 'ABORT'." |
433 print "> ", | 436 print "> ", |
434 answer = self.ReadLine() | 437 answer = self.ReadLine() |
435 | 438 |
436 # Takes a file containing the patch to apply as first argument. | 439 # Takes a file containing the patch to apply as first argument. |
437 def ApplyPatch(self, patch_file, reverse_patch=""): | 440 def ApplyPatch(self, patch_file, reverse_patch=""): |
438 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) | 441 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) |
439 if self.Git(args) is None: | 442 if self.Git(args) is None: |
440 self.WaitForResolvingConflicts(patch_file) | 443 self.WaitForResolvingConflicts(patch_file) |
441 | 444 |
442 | 445 |
443 class UploadStep(Step): | 446 class UploadStep(Step): |
444 MESSAGE = "Upload for code review." | 447 MESSAGE = "Upload for code review." |
445 | 448 |
446 def RunStep(self): | 449 def RunStep(self): |
447 if self._options.r: | 450 if self._options.r: |
448 print "Using account %s for review." % self._options.r | 451 print "Using account %s for review." % self._options.r |
449 reviewer = self._options.r | 452 reviewer = self._options.r |
450 else: | 453 else: |
451 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: ", |
452 self.DieInForcedMode("A reviewer must be specified in forced mode.") | 455 self.DieNoManualMode("A reviewer must be specified in forced mode.") |
453 reviewer = self.ReadLine() | 456 reviewer = self.ReadLine() |
454 force_flag = " -f" if self._options.f else "" | 457 force_flag = " -f" if not self.IsManual() else "" |
455 args = "cl upload -r \"%s\" --send-mail%s" % (reviewer, force_flag) | 458 args = "cl upload -r \"%s\" --send-mail%s" % (reviewer, force_flag) |
456 # TODO(machenbach): Check output in forced mode. Verify that all required | 459 # TODO(machenbach): Check output in forced mode. Verify that all required |
457 # base files were uploaded, if not retry. | 460 # base files were uploaded, if not retry. |
458 if self.Git(args, pipe=False) is None: | 461 if self.Git(args, pipe=False) is None: |
459 self.Die("'git cl upload' failed, please try again.") | 462 self.Die("'git cl upload' failed, please try again.") |
460 | 463 |
461 | 464 |
462 def MakeStep(step_class=Step, number=0, state=None, config=None, | 465 def MakeStep(step_class=Step, number=0, state=None, config=None, |
463 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): | 466 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): |
464 # Allow to pass in empty dictionaries. | 467 # Allow to pass in empty dictionaries. |
(...skipping 19 matching lines...) Expand all Loading... |
484 options, | 487 options, |
485 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): | 488 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): |
486 state = {} | 489 state = {} |
487 steps = [] | 490 steps = [] |
488 for (number, step_class) in enumerate(step_classes): | 491 for (number, step_class) in enumerate(step_classes): |
489 steps.append(MakeStep(step_class, number, state, config, | 492 steps.append(MakeStep(step_class, number, state, config, |
490 options, side_effect_handler)) | 493 options, side_effect_handler)) |
491 | 494 |
492 for step in steps[options.s:]: | 495 for step in steps[options.s:]: |
493 step.Run() | 496 step.Run() |
OLD | NEW |