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 311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
349 return ['%s/%s' % (f[2], f[3]) for f in files_info if f[0] != 'A'] | 355 return ['%s/%s' % (f[2], f[3]) for f in files_info if f[0] != 'A'] |
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 |
M-A Ruel
2011/11/11 21:23:24
another empty line
James Hawkins
2011/11/11 21:50:25
Done.
| |
366 def getBranchForMilestone(milestone): | |
367 """Queries omahaproxy.appspot.com for the branch number given |milestone|. | |
368 """ | |
369 OMAHA_PROXY_URL = "http://omahaproxy.appspot.com" | |
370 request = urllib2.Request(OMAHA_PROXY_URL) | |
371 try: | |
372 response = urllib2.urlopen(request) | |
373 except urllib2.HTTPError, e: | |
374 print "Failed to query %s: %d" % (OMAHA_PROXY_URL, e.code) | |
375 return None | |
376 | |
377 # Dictionary of [branch: major]. When searching for the appropriate branch | |
378 # matching |milestone|, all major versions that match are added to the | |
379 # dictionary. If all of the branches are the same, this branch value is | |
380 # returned; otherwise, the user is prompted to accept the largest branch | |
381 # value. | |
382 branch_dict = {} | |
383 | |
384 # Slice the first line since it's column information text. | |
385 for line in response.readlines()[1:]: | |
386 # Version data is CSV. | |
387 parameters = string.split(line, ',') | |
388 | |
389 # Version is the third parameter and consists of a quad of numbers separated | |
390 # by periods. | |
391 version = string.split(parameters[2], '.') | |
392 major = int(version[0], 10) | |
393 if major != milestone: | |
394 continue | |
395 | |
396 # Branch number is the third value in the quad. | |
397 branch_dict[version[2]] = major | |
398 | |
399 if not branch_dict: | |
400 # |milestone| not found. | |
401 print "Milestone provided is invalid" | |
402 return None | |
403 | |
404 # The following returns a sorted list of the keys of |branch_dict|. | |
405 sorted_branches = sorted(branch_dict.iterkeys()) | |
M-A Ruel
2011/11/11 21:23:24
sorted_branches = sorted(branch_dict)
James Hawkins
2011/11/11 21:50:25
Done.
| |
406 branch = sorted_branches[0] | |
407 | |
408 # If all keys match, the branch is the same for all platforms given | |
409 # |milestone|. This is the safe case, so return the branch. | |
410 if len(sorted_branches) == 1: | |
411 return branch | |
412 | |
413 # Not all of the platforms have the same branch. Prompt the user and return | |
414 # the greatest (by value) branch on success. | |
415 if prompt("Not all platforms have the same branch number, " | |
416 "continue with branch %s?" % branch): | |
417 return branch | |
418 | |
419 # User cancelled. | |
420 return None | |
421 | |
M-A Ruel
2011/11/11 21:23:24
another empty line
James Hawkins
2011/11/11 21:50:25
Done.
| |
360 def prompt(question): | 422 def prompt(question): |
361 while True: | 423 while True: |
362 print question + " [y|n]:", | 424 print question + " [y|n]:", |
363 answer = sys.stdin.readline() | 425 answer = sys.stdin.readline() |
364 if answer.lower().startswith('n'): | 426 if answer.lower().startswith('n'): |
365 return False | 427 return False |
366 elif answer.lower().startswith('y'): | 428 elif answer.lower().startswith('y'): |
367 return True | 429 return True |
368 | 430 |
369 | 431 |
370 def text_prompt(question, default): | 432 def text_prompt(question, default): |
371 print question + " [" + default + "]:" | 433 print question + " [" + default + "]:" |
372 answer = sys.stdin.readline() | 434 answer = sys.stdin.readline() |
373 if answer.strip() == "": | 435 if answer.strip() == "": |
374 return default | 436 return default |
375 return answer | 437 return answer |
376 | 438 |
377 | 439 |
378 def drover(options, args): | 440 def drover(options, args): |
379 revision = options.revert or options.merge | 441 revision = options.revert or options.merge |
380 | 442 |
381 # Initialize some variables used below. They can be overwritten by | 443 # Initialize some variables used below. They can be overwritten by |
382 # the drover.properties file. | 444 # the drover.properties file. |
383 BASE_URL = "svn://svn.chromium.org/chrome" | 445 BASE_URL = "svn://svn.chromium.org/chrome" |
384 TRUNK_URL = BASE_URL + "/trunk/src" | 446 TRUNK_URL = BASE_URL + "/trunk/src" |
385 BRANCH_URL = BASE_URL + "/branches/$branch/src" | 447 BRANCH_URL = BASE_URL + "/branches/$branch/src" |
386 SKIP_CHECK_WORKING = True | 448 SKIP_CHECK_WORKING = True |
387 PROMPT_FOR_AUTHOR = False | 449 PROMPT_FOR_AUTHOR = False |
388 | 450 |
451 # Translate a given milestone to the appropriate branch number. | |
452 if options.milestone: | |
453 options.branch = getBranchForMilestone(options.milestone) | |
laforge
2011/11/11 21:06:47
I'm not sure that you'll be able to set this value
James Hawkins
2011/11/11 21:11:41
Per-offline, manual tests show that this assignmen
| |
454 if options.branch is None: | |
M-A Ruel
2011/11/11 21:23:24
if not options.branch:
James Hawkins
2011/11/11 21:50:25
Done.
| |
455 return 1 | |
456 | |
389 DEFAULT_WORKING = "drover_" + str(revision) | 457 DEFAULT_WORKING = "drover_" + str(revision) |
390 if options.branch: | 458 if options.branch: |
391 DEFAULT_WORKING += ("_" + options.branch) | 459 DEFAULT_WORKING += ("_" + options.branch) |
392 | 460 |
393 if not isMinimumSVNVersion(1, 5): | 461 if not isMinimumSVNVersion(1, 5): |
394 print "You need to use at least SVN version 1.5.x" | 462 print "You need to use at least SVN version 1.5.x" |
395 return 1 | 463 return 1 |
396 | 464 |
397 # Override the default properties if there is a drover.properties file. | 465 # Override the default properties if there is a drover.properties file. |
398 global file_pattern_ | 466 global file_pattern_ |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
515 else: | 583 else: |
516 return 0 | 584 return 0 |
517 | 585 |
518 | 586 |
519 def main(): | 587 def main(): |
520 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) | 588 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) |
521 option_parser.add_option('-m', '--merge', type="int", | 589 option_parser.add_option('-m', '--merge', type="int", |
522 help='Revision to merge from trunk to branch') | 590 help='Revision to merge from trunk to branch') |
523 option_parser.add_option('-b', '--branch', | 591 option_parser.add_option('-b', '--branch', |
524 help='Branch to revert or merge from') | 592 help='Branch to revert or merge from') |
593 option_parser.add_option('-M', '--milestone', type="int", | |
594 help='Milestone to revert or merge from') | |
525 option_parser.add_option('-l', '--local', action='store_true', | 595 option_parser.add_option('-l', '--local', action='store_true', |
526 help='Local working copy to merge to') | 596 help='Local working copy to merge to') |
527 option_parser.add_option('-s', '--sbranch', | 597 option_parser.add_option('-s', '--sbranch', |
528 help='Source branch for merge') | 598 help='Source branch for merge') |
529 option_parser.add_option('-r', '--revert', type="int", | 599 option_parser.add_option('-r', '--revert', type="int", |
530 help='Revision to revert') | 600 help='Revision to revert') |
531 option_parser.add_option('-w', '--workdir', | 601 option_parser.add_option('-w', '--workdir', |
532 help='subdir to use for the revert') | 602 help='subdir to use for the revert') |
533 option_parser.add_option('-a', '--auditor', | 603 option_parser.add_option('-a', '--auditor', |
534 help='overrides the author for reviewer') | 604 help='overrides the author for reviewer') |
535 option_parser.add_option('', '--revertbot', action='store_true', | 605 option_parser.add_option('', '--revertbot', action='store_true', |
536 default=False) | 606 default=False) |
537 option_parser.add_option('', '--revertbot-commit', action='store_true', | 607 option_parser.add_option('', '--revertbot-commit', action='store_true', |
538 default=False) | 608 default=False) |
539 option_parser.add_option('', '--revertbot-reviewers') | 609 option_parser.add_option('', '--revertbot-reviewers') |
540 options, args = option_parser.parse_args() | 610 options, args = option_parser.parse_args() |
541 | 611 |
542 if not options.merge and not options.revert: | 612 if not options.merge and not options.revert: |
543 option_parser.error("You need at least --merge or --revert") | 613 option_parser.error("You need at least --merge or --revert") |
544 return 1 | 614 return 1 |
545 | 615 |
M-A Ruel
2011/11/11 21:23:24
optional: maybe:
reference = bool(options.branch o
James Hawkins
2011/11/11 21:50:25
Done.
| |
546 if options.merge and not options.branch and not options.local: | 616 if (options.merge and not options.branch and not options.milestone and |
617 not options.local): | |
547 option_parser.error("--merge requires either --branch or --local") | 618 option_parser.error("--merge requires either --branch or --local") |
M-A Ruel
2011/11/11 21:23:24
or --milestone ?
James Hawkins
2011/11/11 21:50:25
Done.
| |
548 return 1 | 619 return 1 |
549 | 620 |
550 if options.local and (options.revert or options.branch): | 621 if options.local and (options.revert or options.branch or options.milestone): |
551 option_parser.error("--local cannot be used with --revert or --branch") | 622 option_parser.error("--local cannot be used with --revert or --branch") |
M-A Ruel
2011/11/11 21:23:24
or --milestone
James Hawkins
2011/11/11 21:50:25
Done.
| |
552 return 1 | 623 return 1 |
553 | 624 |
625 if options.branch and options.milestone: | |
626 option_parser.error("--branch cannot be used with --milestone") | |
627 return 1 | |
628 | |
554 return drover(options, args) | 629 return drover(options, args) |
555 | 630 |
556 | 631 |
557 if __name__ == "__main__": | 632 if __name__ == "__main__": |
558 sys.exit(main()) | 633 sys.exit(main()) |
OLD | NEW |