| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import optparse | 6 import optparse |
| 7 import os | 7 import os |
| 8 import re | 8 import re |
| 9 import string |
| 9 import sys | 10 import sys |
| 11 import urllib2 |
| 10 | 12 |
| 11 import breakpad # pylint: disable=W0611 | 13 import breakpad # pylint: disable=W0611 |
| 12 | 14 |
| 13 import gclient_utils | 15 import gclient_utils |
| 14 import subprocess2 | 16 import subprocess2 |
| 15 | 17 |
| 16 USAGE = """ | 18 USAGE = """ |
| 17 WARNING: Please use this tool in an empty directory | 19 WARNING: Please use this tool in an empty directory |
| 18 (or at least one that you don't mind clobbering.) | 20 (or at least one that you don't mind clobbering.) |
| 19 | 21 |
| 20 REQUIRES: SVN 1.5+ | 22 REQUIRES: SVN 1.5+ |
| 21 NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL. | 23 NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL. |
| 22 Valid parameters: | 24 Valid parameters: |
| 23 | 25 |
| 24 [Merge from trunk to branch] | 26 [Merge from trunk to branch] |
| 25 --merge <revision> --branch <branch_num> | 27 --merge <revision> --branch <branch_num> |
| 26 Example: %(app)s --merge 12345 --branch 187 | 28 Example: %(app)s --merge 12345 --branch 187 |
| 27 | 29 |
| 30 [Merge from trunk to milestone] |
| 31 --merge <revision> --milestone <milestone_num> |
| 32 Example: %(app)s -- merge 12345 --milestone 16 |
| 33 |
| 28 [Merge from trunk to local copy] | 34 [Merge from trunk to local copy] |
| 29 --merge <revision> --local | 35 --merge <revision> --local |
| 30 Example: %(app)s --merge 12345 --local | 36 Example: %(app)s --merge 12345 --local |
| 31 | 37 |
| 32 [Merge from branch to branch] | 38 [Merge from branch to branch] |
| 33 --merge <revision> --sbranch <branch_num> --branch <branch_num> | 39 --merge <revision> --sbranch <branch_num> --branch <branch_num> |
| 34 Example: %(app)s --merge 12345 --sbranch 248 --branch 249 | 40 Example: %(app)s --merge 12345 --sbranch 248 --branch 249 |
| 35 | 41 |
| 36 [Revert from trunk] | 42 [Revert from trunk] |
| 37 --revert <revision> | 43 --revert <revision> |
| (...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 350 | 356 |
| 351 | 357 |
| 352 def getAllFilesInRevision(files_info): | 358 def getAllFilesInRevision(files_info): |
| 353 """Checks for existing files in the revision. | 359 """Checks for existing files in the revision. |
| 354 | 360 |
| 355 Anything that's A will require special treatment (either a merge or an | 361 Anything that's A will require special treatment (either a merge or an |
| 356 export + add) | 362 export + add) |
| 357 """ | 363 """ |
| 358 return ['%s/%s' % (f[2], f[3]) for f in files_info] | 364 return ['%s/%s' % (f[2], f[3]) for f in files_info] |
| 359 | 365 |
| 366 |
| 367 def getBranchForMilestone(milestone): |
| 368 """Queries omahaproxy.appspot.com for the branch number given |milestone|. |
| 369 """ |
| 370 OMAHA_PROXY_URL = "http://omahaproxy.appspot.com" |
| 371 request = urllib2.Request(OMAHA_PROXY_URL) |
| 372 try: |
| 373 response = urllib2.urlopen(request) |
| 374 except urllib2.HTTPError, e: |
| 375 print "Failed to query %s: %d" % (OMAHA_PROXY_URL, e.code) |
| 376 return None |
| 377 |
| 378 # Dictionary of [branch: major]. When searching for the appropriate branch |
| 379 # matching |milestone|, all major versions that match are added to the |
| 380 # dictionary. If all of the branches are the same, this branch value is |
| 381 # returned; otherwise, the user is prompted to accept the largest branch |
| 382 # value. |
| 383 branch_dict = {} |
| 384 |
| 385 # Slice the first line since it's column information text. |
| 386 for line in response.readlines()[1:]: |
| 387 # Version data is CSV. |
| 388 parameters = string.split(line, ',') |
| 389 |
| 390 # Version is the third parameter and consists of a quad of numbers separated |
| 391 # by periods. |
| 392 version = string.split(parameters[2], '.') |
| 393 major = int(version[0], 10) |
| 394 if major != milestone: |
| 395 continue |
| 396 |
| 397 # Branch number is the third value in the quad. |
| 398 branch_dict[version[2]] = major |
| 399 |
| 400 if not branch_dict: |
| 401 # |milestone| not found. |
| 402 print "Milestone provided is invalid" |
| 403 return None |
| 404 |
| 405 # The following returns a sorted list of the keys of |branch_dict|. |
| 406 sorted_branches = sorted(branch_dict) |
| 407 branch = sorted_branches[0] |
| 408 |
| 409 # If all keys match, the branch is the same for all platforms given |
| 410 # |milestone|. This is the safe case, so return the branch. |
| 411 if len(sorted_branches) == 1: |
| 412 return branch |
| 413 |
| 414 # Not all of the platforms have the same branch. Prompt the user and return |
| 415 # the greatest (by value) branch on success. |
| 416 if prompt("Not all platforms have the same branch number, " |
| 417 "continue with branch %s?" % branch): |
| 418 return branch |
| 419 |
| 420 # User cancelled. |
| 421 return None |
| 422 |
| 423 |
| 360 def prompt(question): | 424 def prompt(question): |
| 361 while True: | 425 while True: |
| 362 print question + " [y|n]:", | 426 print question + " [y|n]:", |
| 363 answer = sys.stdin.readline() | 427 answer = sys.stdin.readline() |
| 364 if answer.lower().startswith('n'): | 428 if answer.lower().startswith('n'): |
| 365 return False | 429 return False |
| 366 elif answer.lower().startswith('y'): | 430 elif answer.lower().startswith('y'): |
| 367 return True | 431 return True |
| 368 | 432 |
| 369 | 433 |
| 370 def text_prompt(question, default): | 434 def text_prompt(question, default): |
| 371 print question + " [" + default + "]:" | 435 print question + " [" + default + "]:" |
| 372 answer = sys.stdin.readline() | 436 answer = sys.stdin.readline() |
| 373 if answer.strip() == "": | 437 if answer.strip() == "": |
| 374 return default | 438 return default |
| 375 return answer | 439 return answer |
| 376 | 440 |
| 377 | 441 |
| 378 def drover(options, args): | 442 def drover(options, args): |
| 379 revision = options.revert or options.merge | 443 revision = options.revert or options.merge |
| 380 | 444 |
| 381 # Initialize some variables used below. They can be overwritten by | 445 # Initialize some variables used below. They can be overwritten by |
| 382 # the drover.properties file. | 446 # the drover.properties file. |
| 383 BASE_URL = "svn://svn.chromium.org/chrome" | 447 BASE_URL = "svn://svn.chromium.org/chrome" |
| 384 TRUNK_URL = BASE_URL + "/trunk/src" | 448 TRUNK_URL = BASE_URL + "/trunk/src" |
| 385 BRANCH_URL = BASE_URL + "/branches/$branch/src" | 449 BRANCH_URL = BASE_URL + "/branches/$branch/src" |
| 386 SKIP_CHECK_WORKING = True | 450 SKIP_CHECK_WORKING = True |
| 387 PROMPT_FOR_AUTHOR = False | 451 PROMPT_FOR_AUTHOR = False |
| 388 | 452 |
| 453 # Translate a given milestone to the appropriate branch number. |
| 454 if options.milestone: |
| 455 options.branch = getBranchForMilestone(options.milestone) |
| 456 if not options.branch: |
| 457 return 1 |
| 458 |
| 389 DEFAULT_WORKING = "drover_" + str(revision) | 459 DEFAULT_WORKING = "drover_" + str(revision) |
| 390 if options.branch: | 460 if options.branch: |
| 391 DEFAULT_WORKING += ("_" + options.branch) | 461 DEFAULT_WORKING += ("_" + options.branch) |
| 392 | 462 |
| 393 if not isMinimumSVNVersion(1, 5): | 463 if not isMinimumSVNVersion(1, 5): |
| 394 print "You need to use at least SVN version 1.5.x" | 464 print "You need to use at least SVN version 1.5.x" |
| 395 return 1 | 465 return 1 |
| 396 | 466 |
| 397 # Override the default properties if there is a drover.properties file. | 467 # Override the default properties if there is a drover.properties file. |
| 398 global file_pattern_ | 468 global file_pattern_ |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 515 else: | 585 else: |
| 516 return 0 | 586 return 0 |
| 517 | 587 |
| 518 | 588 |
| 519 def main(): | 589 def main(): |
| 520 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) | 590 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) |
| 521 option_parser.add_option('-m', '--merge', type="int", | 591 option_parser.add_option('-m', '--merge', type="int", |
| 522 help='Revision to merge from trunk to branch') | 592 help='Revision to merge from trunk to branch') |
| 523 option_parser.add_option('-b', '--branch', | 593 option_parser.add_option('-b', '--branch', |
| 524 help='Branch to revert or merge from') | 594 help='Branch to revert or merge from') |
| 595 option_parser.add_option('-M', '--milestone', type="int", |
| 596 help='Milestone to revert or merge from') |
| 525 option_parser.add_option('-l', '--local', action='store_true', | 597 option_parser.add_option('-l', '--local', action='store_true', |
| 526 help='Local working copy to merge to') | 598 help='Local working copy to merge to') |
| 527 option_parser.add_option('-s', '--sbranch', | 599 option_parser.add_option('-s', '--sbranch', |
| 528 help='Source branch for merge') | 600 help='Source branch for merge') |
| 529 option_parser.add_option('-r', '--revert', type="int", | 601 option_parser.add_option('-r', '--revert', type="int", |
| 530 help='Revision to revert') | 602 help='Revision to revert') |
| 531 option_parser.add_option('-w', '--workdir', | 603 option_parser.add_option('-w', '--workdir', |
| 532 help='subdir to use for the revert') | 604 help='subdir to use for the revert') |
| 533 option_parser.add_option('-a', '--auditor', | 605 option_parser.add_option('-a', '--auditor', |
| 534 help='overrides the author for reviewer') | 606 help='overrides the author for reviewer') |
| 535 option_parser.add_option('', '--revertbot', action='store_true', | 607 option_parser.add_option('', '--revertbot', action='store_true', |
| 536 default=False) | 608 default=False) |
| 537 option_parser.add_option('', '--revertbot-commit', action='store_true', | 609 option_parser.add_option('', '--revertbot-commit', action='store_true', |
| 538 default=False) | 610 default=False) |
| 539 option_parser.add_option('', '--revertbot-reviewers') | 611 option_parser.add_option('', '--revertbot-reviewers') |
| 540 options, args = option_parser.parse_args() | 612 options, args = option_parser.parse_args() |
| 541 | 613 |
| 542 if not options.merge and not options.revert: | 614 if not options.merge and not options.revert: |
| 543 option_parser.error("You need at least --merge or --revert") | 615 option_parser.error("You need at least --merge or --revert") |
| 544 return 1 | 616 return 1 |
| 545 | 617 |
| 546 if options.merge and not options.branch and not options.local: | 618 if options.merge and not (options.branch or options.milestone or |
| 547 option_parser.error("--merge requires either --branch or --local") | 619 options.local): |
| 620 option_parser.error("--merge requires either --branch " |
| 621 "or --milestone or --local") |
| 548 return 1 | 622 return 1 |
| 549 | 623 |
| 550 if options.local and (options.revert or options.branch): | 624 if options.local and (options.revert or options.branch or options.milestone): |
| 551 option_parser.error("--local cannot be used with --revert or --branch") | 625 option_parser.error("--local cannot be used with --revert " |
| 626 "or --branch or --milestone") |
| 627 return 1 |
| 628 |
| 629 if options.branch and options.milestone: |
| 630 option_parser.error("--branch cannot be used with --milestone") |
| 552 return 1 | 631 return 1 |
| 553 | 632 |
| 554 return drover(options, args) | 633 return drover(options, args) |
| 555 | 634 |
| 556 | 635 |
| 557 if __name__ == "__main__": | 636 if __name__ == "__main__": |
| 558 sys.exit(main()) | 637 sys.exit(main()) |
| OLD | NEW |