OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 collections | |
7 import datetime | 6 import datetime |
8 import json | |
9 import optparse | 7 import optparse |
10 import os | 8 import os |
11 import re | 9 import re |
12 import sys | 10 import sys |
13 import urllib2 | |
14 import urlparse | 11 import urlparse |
15 | 12 |
16 import breakpad # pylint: disable=W0611 | 13 import breakpad # pylint: disable=W0611 |
17 | 14 |
18 import gclient_utils | 15 import gclient_utils |
19 import subprocess2 | 16 import subprocess2 |
20 | 17 |
21 USAGE = """ | 18 USAGE = """ |
22 WARNING: Please use this tool in an empty directory | 19 WARNING: Please use this tool in an empty directory |
23 (or at least one that you don't mind clobbering.) | 20 (or at least one that you don't mind clobbering.) |
24 | 21 |
25 REQUIRES: SVN 1.5+ | 22 REQUIRES: SVN 1.5+ |
26 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. |
27 Valid parameters: | 24 Valid parameters: |
28 | 25 |
29 [Merge from trunk to branch] | 26 [Merge from trunk to branch] |
30 --merge <revision> --branch <branch_num> | 27 --merge <revision> --branch <branch_num> |
31 Example: %(app)s --merge 12345 --branch 187 | 28 Example: %(app)s --merge 12345 --branch 187 |
32 | 29 |
33 [Merge from trunk to milestone] | |
34 --merge <revision> --milestone <milestone_num> | |
35 Example: %(app)s --merge 12345 --milestone 16 | |
36 | |
37 [Merge from trunk to local copy] | 30 [Merge from trunk to local copy] |
38 --merge <revision> --local | 31 --merge <revision> --local |
39 Example: %(app)s --merge 12345 --local | 32 Example: %(app)s --merge 12345 --local |
40 | 33 |
41 [Merge from branch to branch] | 34 [Merge from branch to branch] |
42 --merge <revision> --sbranch <branch_num> --branch <branch_num> | 35 --merge <revision> --sbranch <branch_num> --branch <branch_num> |
43 Example: %(app)s --merge 12345 --sbranch 248 --branch 249 | 36 Example: %(app)s --merge 12345 --sbranch 248 --branch 249 |
44 | 37 |
45 [Revert from trunk] | 38 [Revert from trunk] |
46 --revert <revision> | 39 --revert <revision> |
(...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 | 356 |
364 def getAllFilesInRevision(files_info): | 357 def getAllFilesInRevision(files_info): |
365 """Checks for existing files in the revision. | 358 """Checks for existing files in the revision. |
366 | 359 |
367 Anything that's A will require special treatment (either a merge or an | 360 Anything that's A will require special treatment (either a merge or an |
368 export + add) | 361 export + add) |
369 """ | 362 """ |
370 return ['%s/%s' % (f[2], f[3]) for f in files_info] | 363 return ['%s/%s' % (f[2], f[3]) for f in files_info] |
371 | 364 |
372 | 365 |
373 def getBranchForMilestone(milestone): | |
374 """Queries omahaproxy.appspot.com for the branch number given |milestone|. | |
375 """ | |
376 OMAHA_PROXY_URL = "https://omahaproxy.appspot.com/all?json=1" | |
377 try: | |
378 response = urllib2.urlopen(OMAHA_PROXY_URL) | |
379 except urllib2.HTTPError, e: | |
380 print "Failed to query %s: %d" % (OMAHA_PROXY_URL, e.code) | |
381 return None | |
382 | |
383 # Response is in the form of: | |
384 # [{ os: "os_name", versions: [{ channel: "canary", true_branch: "1490" }] }] | |
385 os_versions = json.load(response) | |
386 | |
387 branches = collections.defaultdict(list) | |
388 for os_version in os_versions: | |
389 for version in os_version['versions']: | |
390 if not version['true_branch'] or not version['version']: | |
391 continue | |
392 branch = version['true_branch'] | |
393 mstone = version['version'].split('.') | |
394 if not branch[0].isdigit() or mstone[0] != str(milestone): | |
395 continue | |
396 branches[branch] += [os_version['os']] | |
397 | |
398 if not branches: | |
399 return None | |
400 | |
401 if len(branches) == 1: | |
402 return branches.keys()[0] | |
403 | |
404 choices = ('-(%s): %s' % (b, ', '.join(o)) for b, o in branches.iteritems()) | |
405 print >> sys.stderr, ("\nNot all platforms have same branch number for M%d.\n" | |
406 "\nHere's a list of platforms on each branch:\n" | |
407 "%s") % (milestone, '\n'.join(choices)) | |
408 | |
409 errors = 0 | |
410 while errors < 3: | |
411 user_input = raw_input("Which branch? ('q' to cancel) ").strip().lower() | |
412 if user_input in branches: | |
413 return user_input | |
414 if user_input.startswith('q'): | |
415 break | |
416 errors += 1 | |
417 | |
418 return None | |
419 | |
420 def getSVNAuthInfo(folder=None): | 366 def getSVNAuthInfo(folder=None): |
421 """Fetches SVN authorization information in the subversion auth folder and | 367 """Fetches SVN authorization information in the subversion auth folder and |
422 returns it as a dictionary of dictionaries.""" | 368 returns it as a dictionary of dictionaries.""" |
423 if not folder: | 369 if not folder: |
424 if sys.platform == 'win32': | 370 if sys.platform == 'win32': |
425 folder = '%%APPDATA%\\Subversion\\auth' | 371 folder = '%%APPDATA%\\Subversion\\auth' |
426 else: | 372 else: |
427 folder = '~/.subversion/auth' | 373 folder = '~/.subversion/auth' |
428 folder = os.path.expandvars(os.path.expanduser(folder)) | 374 folder = os.path.expandvars(os.path.expanduser(folder)) |
429 svn_simple_folder = os.path.join(folder, 'svn.simple') | 375 svn_simple_folder = os.path.join(folder, 'svn.simple') |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
480 BASE_URL = "svn://svn.chromium.org/chrome" | 426 BASE_URL = "svn://svn.chromium.org/chrome" |
481 REVERT_ALT_URLS = ['svn://svn.chromium.org/blink', | 427 REVERT_ALT_URLS = ['svn://svn.chromium.org/blink', |
482 'svn://svn.chromium.org/chrome-internal', | 428 'svn://svn.chromium.org/chrome-internal', |
483 'svn://svn.chromium.org/native_client'] | 429 'svn://svn.chromium.org/native_client'] |
484 TRUNK_URL = BASE_URL + "/trunk/src" | 430 TRUNK_URL = BASE_URL + "/trunk/src" |
485 BRANCH_URL = BASE_URL + "/branches/$branch/src" | 431 BRANCH_URL = BASE_URL + "/branches/$branch/src" |
486 SKIP_CHECK_WORKING = True | 432 SKIP_CHECK_WORKING = True |
487 PROMPT_FOR_AUTHOR = False | 433 PROMPT_FOR_AUTHOR = False |
488 NO_ALT_URLS = options.no_alt_urls | 434 NO_ALT_URLS = options.no_alt_urls |
489 | 435 |
490 # Translate a given milestone to the appropriate branch number. | |
491 if options.milestone: | |
492 options.branch = getBranchForMilestone(options.milestone) | |
493 if not options.branch: | |
494 return 1 | |
495 | |
496 DEFAULT_WORKING = "drover_" + str(revision) | 436 DEFAULT_WORKING = "drover_" + str(revision) |
497 if options.branch: | 437 if options.branch: |
498 DEFAULT_WORKING += ("_" + options.branch) | 438 DEFAULT_WORKING += ("_" + options.branch) |
499 | 439 |
500 if not isMinimumSVNVersion(1, 5): | 440 if not isMinimumSVNVersion(1, 5): |
501 print "You need to use at least SVN version 1.5.x" | 441 print "You need to use at least SVN version 1.5.x" |
502 return 1 | 442 return 1 |
503 | 443 |
504 # Override the default properties if there is a drover.properties file. | 444 # Override the default properties if there is a drover.properties file. |
505 global file_pattern_ | 445 global file_pattern_ |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
658 else: | 598 else: |
659 return 0 | 599 return 0 |
660 | 600 |
661 | 601 |
662 def main(): | 602 def main(): |
663 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) | 603 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) |
664 option_parser.add_option('-m', '--merge', type="int", | 604 option_parser.add_option('-m', '--merge', type="int", |
665 help='Revision to merge from trunk to branch') | 605 help='Revision to merge from trunk to branch') |
666 option_parser.add_option('-b', '--branch', | 606 option_parser.add_option('-b', '--branch', |
667 help='Branch to revert or merge from') | 607 help='Branch to revert or merge from') |
668 option_parser.add_option('-M', '--milestone', type="int", | |
669 help='Milestone to revert or merge from') | |
670 option_parser.add_option('-l', '--local', action='store_true', | 608 option_parser.add_option('-l', '--local', action='store_true', |
671 help='Local working copy to merge to') | 609 help='Local working copy to merge to') |
672 option_parser.add_option('-s', '--sbranch', | 610 option_parser.add_option('-s', '--sbranch', |
673 help='Source branch for merge') | 611 help='Source branch for merge') |
674 option_parser.add_option('-r', '--revert', type="int", | 612 option_parser.add_option('-r', '--revert', type="int", |
675 help='Revision to revert') | 613 help='Revision to revert') |
676 option_parser.add_option('-w', '--workdir', | 614 option_parser.add_option('-w', '--workdir', |
677 help='subdir to use for the revert') | 615 help='subdir to use for the revert') |
678 option_parser.add_option('-u', '--url', | 616 option_parser.add_option('-u', '--url', |
679 help='svn url to use for the revert') | 617 help='svn url to use for the revert') |
680 option_parser.add_option('-a', '--auditor', | 618 option_parser.add_option('-a', '--auditor', |
681 help='overrides the author for reviewer') | 619 help='overrides the author for reviewer') |
682 option_parser.add_option('--revertbot', action='store_true', | 620 option_parser.add_option('--revertbot', action='store_true', |
683 default=False) | 621 default=False) |
684 option_parser.add_option('--no-alt-urls', action='store_true', | 622 option_parser.add_option('--no-alt-urls', action='store_true', |
685 help='Disable heuristics used to determine svn url') | 623 help='Disable heuristics used to determine svn url') |
686 option_parser.add_option('--revertbot-commit', action='store_true', | 624 option_parser.add_option('--revertbot-commit', action='store_true', |
687 default=False) | 625 default=False) |
688 option_parser.add_option('--revertbot-reviewers') | 626 option_parser.add_option('--revertbot-reviewers') |
689 options, args = option_parser.parse_args() | 627 options, args = option_parser.parse_args() |
690 | 628 |
691 if not options.merge and not options.revert: | 629 if not options.merge and not options.revert: |
692 option_parser.error("You need at least --merge or --revert") | 630 option_parser.error("You need at least --merge or --revert") |
693 return 1 | 631 return 1 |
694 | 632 |
695 if options.merge and not (options.branch or options.milestone or | 633 if options.merge and not (options.branch or options.local): |
696 options.local): | 634 option_parser.error("--merge requires --branch or --local") |
697 option_parser.error("--merge requires either --branch " | |
698 "or --milestone or --local") | |
699 return 1 | 635 return 1 |
700 | 636 |
701 if options.local and (options.revert or options.branch or options.milestone): | 637 if options.local and (options.revert or options.branch): |
702 option_parser.error("--local cannot be used with --revert " | 638 option_parser.error("--local cannot be used with --revert or --branch") |
703 "or --branch or --milestone") | |
704 return 1 | |
705 | |
706 if options.branch and options.milestone: | |
707 option_parser.error("--branch cannot be used with --milestone") | |
708 return 1 | 639 return 1 |
709 | 640 |
710 return drover(options, args) | 641 return drover(options, args) |
711 | 642 |
712 | 643 |
713 if __name__ == "__main__": | 644 if __name__ == "__main__": |
714 sys.exit(main()) | 645 sys.exit(main()) |
OLD | NEW |