| 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 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 213 def GetDate(self): | 213 def GetDate(self): |
| 214 return datetime.date.today().strftime("%Y-%m-%d") | 214 return datetime.date.today().strftime("%Y-%m-%d") |
| 215 | 215 |
| 216 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() | 216 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() |
| 217 | 217 |
| 218 | 218 |
| 219 class NoRetryException(Exception): | 219 class NoRetryException(Exception): |
| 220 pass | 220 pass |
| 221 | 221 |
| 222 | 222 |
| 223 class GitFailedException(Exception): |
| 224 pass |
| 225 |
| 226 |
| 223 class CommonOptions(object): | 227 class CommonOptions(object): |
| 224 def __init__(self, options, manual=True): | 228 def __init__(self, options, manual=True): |
| 225 self.requires_editor = True | 229 self.requires_editor = True |
| 226 self.wait_for_lgtm = True | 230 self.wait_for_lgtm = True |
| 227 self.s = options.s | 231 self.s = options.s |
| 228 self.force_readline_defaults = not manual | 232 self.force_readline_defaults = not manual |
| 229 self.force_upload = not manual | 233 self.force_upload = not manual |
| 230 self.manual = manual | 234 self.manual = manual |
| 231 self.reviewer = getattr(options, 'reviewer', None) | 235 self.reviewer = getattr(options, 'reviewer', None) |
| 232 self.author = getattr(options, 'a', None) | 236 self.author = getattr(options, 'a', None) |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 def ReadLine(self, default=None): | 317 def ReadLine(self, default=None): |
| 314 # Don't prompt in forced mode. | 318 # Don't prompt in forced mode. |
| 315 if self._options.force_readline_defaults and default is not None: | 319 if self._options.force_readline_defaults and default is not None: |
| 316 print "%s (forced)" % default | 320 print "%s (forced)" % default |
| 317 return default | 321 return default |
| 318 else: | 322 else: |
| 319 return self._side_effect_handler.ReadLine() | 323 return self._side_effect_handler.ReadLine() |
| 320 | 324 |
| 321 def Git(self, args="", prefix="", pipe=True, retry_on=None): | 325 def Git(self, args="", prefix="", pipe=True, retry_on=None): |
| 322 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe) | 326 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe) |
| 323 return self.Retry(cmd, retry_on, [5, 30]) | 327 result = self.Retry(cmd, retry_on, [5, 30]) |
| 328 if result is None: |
| 329 raise GitFailedException("'git %s' failed." % args) |
| 330 return result |
| 324 | 331 |
| 325 def SVN(self, args="", prefix="", pipe=True, retry_on=None): | 332 def SVN(self, args="", prefix="", pipe=True, retry_on=None): |
| 326 cmd = lambda: self._side_effect_handler.Command("svn", args, prefix, pipe) | 333 cmd = lambda: self._side_effect_handler.Command("svn", args, prefix, pipe) |
| 327 return self.Retry(cmd, retry_on, [5, 30]) | 334 return self.Retry(cmd, retry_on, [5, 30]) |
| 328 | 335 |
| 329 def Editor(self, args): | 336 def Editor(self, args): |
| 330 if self._options.requires_editor: | 337 if self._options.requires_editor: |
| 331 return self._side_effect_handler.Command(os.environ["EDITOR"], args, | 338 return self._side_effect_handler.Command(os.environ["EDITOR"], args, |
| 332 pipe=False) | 339 pipe=False) |
| 333 | 340 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 354 print "%s [Y/n] " % msg, | 361 print "%s [Y/n] " % msg, |
| 355 answer = self.ReadLine(default="Y") | 362 answer = self.ReadLine(default="Y") |
| 356 return answer == "" or answer == "Y" or answer == "y" | 363 return answer == "" or answer == "Y" or answer == "y" |
| 357 | 364 |
| 358 def DeleteBranch(self, name): | 365 def DeleteBranch(self, name): |
| 359 git_result = self.Git("branch").strip() | 366 git_result = self.Git("branch").strip() |
| 360 for line in git_result.splitlines(): | 367 for line in git_result.splitlines(): |
| 361 if re.match(r".*\s+%s$" % name, line): | 368 if re.match(r".*\s+%s$" % name, line): |
| 362 msg = "Branch %s exists, do you want to delete it?" % name | 369 msg = "Branch %s exists, do you want to delete it?" % name |
| 363 if self.Confirm(msg): | 370 if self.Confirm(msg): |
| 364 if self.Git("branch -D %s" % name) is None: | 371 self.Git("branch -D %s" % name) |
| 365 self.Die("Deleting branch '%s' failed." % name) | |
| 366 print "Branch %s deleted." % name | 372 print "Branch %s deleted." % name |
| 367 else: | 373 else: |
| 368 msg = "Can't continue. Please delete branch %s and try again." % name | 374 msg = "Can't continue. Please delete branch %s and try again." % name |
| 369 self.Die(msg) | 375 self.Die(msg) |
| 370 | 376 |
| 371 def InitialEnvironmentChecks(self): | 377 def InitialEnvironmentChecks(self): |
| 372 # Cancel if this is not a git checkout. | 378 # Cancel if this is not a git checkout. |
| 373 if not os.path.exists(self._config[DOT_GIT_LOCATION]): | 379 if not os.path.exists(self._config[DOT_GIT_LOCATION]): |
| 374 self.Die("This is not a git checkout, this script won't work for you.") | 380 self.Die("This is not a git checkout, this script won't work for you.") |
| 375 | 381 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 386 # Persist current branch. | 392 # Persist current branch. |
| 387 self["current_branch"] = "" | 393 self["current_branch"] = "" |
| 388 git_result = self.Git("status -s -b -uno").strip() | 394 git_result = self.Git("status -s -b -uno").strip() |
| 389 for line in git_result.splitlines(): | 395 for line in git_result.splitlines(): |
| 390 match = re.match(r"^## (.+)", line) | 396 match = re.match(r"^## (.+)", line) |
| 391 if match: | 397 if match: |
| 392 self["current_branch"] = match.group(1) | 398 self["current_branch"] = match.group(1) |
| 393 break | 399 break |
| 394 | 400 |
| 395 # Fetch unfetched revisions. | 401 # Fetch unfetched revisions. |
| 396 if self.Git("svn fetch") is None: | 402 self.Git("svn fetch") |
| 397 self.Die("'git svn fetch' failed.") | |
| 398 | 403 |
| 399 def PrepareBranch(self): | 404 def PrepareBranch(self): |
| 400 # Get ahold of a safe temporary branch and check it out. | 405 # Get ahold of a safe temporary branch and check it out. |
| 401 if self["current_branch"] != self._config[TEMP_BRANCH]: | 406 if self["current_branch"] != self._config[TEMP_BRANCH]: |
| 402 self.DeleteBranch(self._config[TEMP_BRANCH]) | 407 self.DeleteBranch(self._config[TEMP_BRANCH]) |
| 403 self.Git("checkout -b %s" % self._config[TEMP_BRANCH]) | 408 self.Git("checkout -b %s" % self._config[TEMP_BRANCH]) |
| 404 | 409 |
| 405 # Delete the branch that will be created later if it exists already. | 410 # Delete the branch that will be created later if it exists already. |
| 406 self.DeleteBranch(self._config[BRANCHNAME]) | 411 self.DeleteBranch(self._config[BRANCHNAME]) |
| 407 | 412 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 450 if answer == "ABORT": | 455 if answer == "ABORT": |
| 451 self.Die("Applying the patch failed.") | 456 self.Die("Applying the patch failed.") |
| 452 if answer != "": | 457 if answer != "": |
| 453 print "That was not 'RESOLVED' or 'ABORT'." | 458 print "That was not 'RESOLVED' or 'ABORT'." |
| 454 print "> ", | 459 print "> ", |
| 455 answer = self.ReadLine() | 460 answer = self.ReadLine() |
| 456 | 461 |
| 457 # Takes a file containing the patch to apply as first argument. | 462 # Takes a file containing the patch to apply as first argument. |
| 458 def ApplyPatch(self, patch_file, reverse_patch=""): | 463 def ApplyPatch(self, patch_file, reverse_patch=""): |
| 459 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) | 464 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) |
| 460 if self.Git(args) is None: | 465 try: |
| 466 self.Git(args) |
| 467 except GitFailedException: |
| 461 self.WaitForResolvingConflicts(patch_file) | 468 self.WaitForResolvingConflicts(patch_file) |
| 462 | 469 |
| 463 def FindLastTrunkPush(self): | 470 def FindLastTrunkPush(self): |
| 464 push_pattern = "^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based" | 471 push_pattern = "^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based" |
| 465 args = "log -1 --format=%%H --grep=\"%s\" svn/trunk" % push_pattern | 472 args = "log -1 --format=%%H --grep=\"%s\" svn/trunk" % push_pattern |
| 466 return self.Git(args).strip() | 473 return self.Git(args).strip() |
| 467 | 474 |
| 468 | 475 |
| 469 class UploadStep(Step): | 476 class UploadStep(Step): |
| 470 MESSAGE = "Upload for code review." | 477 MESSAGE = "Upload for code review." |
| 471 | 478 |
| 472 def RunStep(self): | 479 def RunStep(self): |
| 473 if self._options.reviewer: | 480 if self._options.reviewer: |
| 474 print "Using account %s for review." % self._options.reviewer | 481 print "Using account %s for review." % self._options.reviewer |
| 475 reviewer = self._options.reviewer | 482 reviewer = self._options.reviewer |
| 476 else: | 483 else: |
| 477 print "Please enter the email address of a V8 reviewer for your patch: ", | 484 print "Please enter the email address of a V8 reviewer for your patch: ", |
| 478 self.DieNoManualMode("A reviewer must be specified in forced mode.") | 485 self.DieNoManualMode("A reviewer must be specified in forced mode.") |
| 479 reviewer = self.ReadLine() | 486 reviewer = self.ReadLine() |
| 480 author_option = self._options.author | 487 author_option = self._options.author |
| 481 author = " --email \"%s\"" % author_option if author_option else "" | 488 author = " --email \"%s\"" % author_option if author_option else "" |
| 482 force_flag = " -f" if self._options.force_upload else "" | 489 force_flag = " -f" if self._options.force_upload else "" |
| 483 args = ("cl upload%s -r \"%s\" --send-mail%s" | 490 args = ("cl upload%s -r \"%s\" --send-mail%s" |
| 484 % (author, reviewer, force_flag)) | 491 % (author, reviewer, force_flag)) |
| 485 # TODO(machenbach): Check output in forced mode. Verify that all required | 492 # TODO(machenbach): Check output in forced mode. Verify that all required |
| 486 # base files were uploaded, if not retry. | 493 # base files were uploaded, if not retry. |
| 487 if self.Git(args, pipe=False) is None: | 494 self.Git(args, pipe=False) |
| 488 self.Die("'git cl upload' failed, please try again.") | |
| 489 | 495 |
| 490 | 496 |
| 491 def MakeStep(step_class=Step, number=0, state=None, config=None, | 497 def MakeStep(step_class=Step, number=0, state=None, config=None, |
| 492 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): | 498 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): |
| 493 # Allow to pass in empty dictionaries. | 499 # Allow to pass in empty dictionaries. |
| 494 state = state if state is not None else {} | 500 state = state if state is not None else {} |
| 495 config = config if config is not None else {} | 501 config = config if config is not None else {} |
| 496 | 502 |
| 497 try: | 503 try: |
| 498 message = step_class.MESSAGE | 504 message = step_class.MESSAGE |
| (...skipping 17 matching lines...) Expand all Loading... |
| 516 if options.s == 0 and os.path.exists(state_file): | 522 if options.s == 0 and os.path.exists(state_file): |
| 517 os.remove(state_file) | 523 os.remove(state_file) |
| 518 state = {} | 524 state = {} |
| 519 steps = [] | 525 steps = [] |
| 520 for (number, step_class) in enumerate(step_classes): | 526 for (number, step_class) in enumerate(step_classes): |
| 521 steps.append(MakeStep(step_class, number, state, config, | 527 steps.append(MakeStep(step_class, number, state, config, |
| 522 options, side_effect_handler)) | 528 options, side_effect_handler)) |
| 523 | 529 |
| 524 for step in steps[options.s:]: | 530 for step in steps[options.s:]: |
| 525 step.Run() | 531 step.Run() |
| OLD | NEW |