Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python2 | 1 #!/usr/bin/python2 |
| 2 | 2 |
| 3 # Copyright 2014 Google Inc. | 3 # Copyright 2014 Google Inc. |
| 4 # | 4 # |
| 5 # Use of this source code is governed by a BSD-style license that can be | 5 # Use of this source code is governed by a BSD-style license that can be |
| 6 # found in the LICENSE file. | 6 # found in the LICENSE file. |
| 7 | 7 |
| 8 """Skia's Chromium DEPS roll script. | 8 """Skia's Chromium DEPS roll script. |
| 9 | 9 |
| 10 This script: | 10 This script: |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 118 ' a temporary will be cloned. Defaults to SKIA_GIT_CHECKOUT' | 118 ' a temporary will be cloned. Defaults to SKIA_GIT_CHECKOUT' |
| 119 '_PATH, if that environment variable is set.', | 119 '_PATH, if that environment variable is set.', |
| 120 default=os.environ.get('SKIA_GIT_CHECKOUT_PATH')) | 120 default=os.environ.get('SKIA_GIT_CHECKOUT_PATH')) |
| 121 option_parser.add_option( | 121 option_parser.add_option( |
| 122 '', '--search_depth', type='int', default=100, | 122 '', '--search_depth', type='int', default=100, |
| 123 help='How far back to look for the revision.') | 123 help='How far back to look for the revision.') |
| 124 option_parser.add_option( | 124 option_parser.add_option( |
| 125 '', '--git_path', help='Git executable, defaults to "git".', | 125 '', '--git_path', help='Git executable, defaults to "git".', |
| 126 default='git') | 126 default='git') |
| 127 option_parser.add_option( | 127 option_parser.add_option( |
| 128 '', '--save_branches', help='Save the temporary branches', | 128 '', '--save_branches', |
| 129 action='store_true', dest='save_branches', default=False) | 129 help='Save the temporary branches (default)', |
| 130 action='store_true', dest='save_branches', default=True) | |
| 131 option_parser.add_option( | |
| 132 '', '--delete_branches', help='Delete the temporary branches', | |
| 133 action='store_false', dest='save_branches', default=True) | |
|
borenet
2014/01/07 21:39:34
If these are changing the same variable, why do we
hal.canary
2014/01/08 18:57:38
Done.
| |
| 130 option_parser.add_option( | 134 option_parser.add_option( |
| 131 '', '--verbose', help='Do not suppress the output from `git cl`.', | 135 '', '--verbose', help='Do not suppress the output from `git cl`.', |
| 132 action='store_true', dest='verbose', default=False) | 136 action='store_true', dest='verbose', default=False) |
| 133 option_parser.add_option( | 137 option_parser.add_option( |
| 134 '', '--skip_cl_upload', help='Skip the cl upload step; useful' | 138 '', '--skip_cl_upload', help='Skip the cl upload step; useful' |
| 135 ' for testing or with --save_branches.', | 139 ' for testing or with --save_branches.', |
| 136 action='store_true', default=False) | 140 action='store_true', default=False) |
| 137 | 141 |
| 138 default_bots_help = ( | 142 default_bots_help = ( |
| 139 'Comma-separated list of bots, defaults to a list of %d bots.' | 143 'Comma-separated list of bots, defaults to a list of %d bots.' |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 261 'origin/master'], cwd=skia_dir) | 265 'origin/master'], cwd=skia_dir) |
| 262 | 266 |
| 263 if revision < 0 or not git_hash: | 267 if revision < 0 or not git_hash: |
| 264 raise DepsRollError('Git hash can not be found.') | 268 raise DepsRollError('Git hash can not be found.') |
| 265 return revision, git_hash | 269 return revision, git_hash |
| 266 finally: | 270 finally: |
| 267 if use_temp: | 271 if use_temp: |
| 268 shutil.rmtree(skia_dir) | 272 shutil.rmtree(skia_dir) |
| 269 | 273 |
| 270 | 274 |
| 275 class ChangeDir(object): | |
| 276 """Use with a with-statement to temporarily change directories.""" | |
| 277 # pylint: disable=I0011,R0903 | |
| 278 | |
| 279 def __init__(self, directory): | |
| 280 self._directory = directory | |
| 281 | |
| 282 def __enter__(self): | |
| 283 cwd = os.getcwd() | |
| 284 os.chdir(self._directory) | |
| 285 self._directory = cwd | |
| 286 | |
| 287 def __exit__(self, etype, value, traceback): | |
| 288 os.chdir(self._directory) | |
| 289 | |
| 290 | |
| 271 class GitBranchCLUpload(object): | 291 class GitBranchCLUpload(object): |
| 272 """Class to manage git branches and git-cl-upload. | 292 """Class to manage git branches and git-cl-upload. |
| 273 | 293 |
| 274 This class allows one to create a new branch in a repository based | 294 This class allows one to create a new branch in a repository based |
| 275 off of origin/master, make changes to the tree inside the | 295 off of origin/master, make changes to the tree inside the |
| 276 with-block, upload that new branch to Rietveld, restore the original | 296 with-block, upload that new branch to Rietveld, restore the original |
| 277 tree state, and delete the local copy of the new branch. | 297 tree state, and delete the local copy of the new branch. |
| 278 | 298 |
| 279 See roll_deps() for an example of use. | 299 See roll_deps() for an example of use. |
| 280 | 300 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 306 self.issue = None | 326 self.issue = None |
| 307 | 327 |
| 308 def stage_for_commit(self, *paths): | 328 def stage_for_commit(self, *paths): |
| 309 """Calls `git add ...` on each argument. | 329 """Calls `git add ...` on each argument. |
| 310 | 330 |
| 311 Args: | 331 Args: |
| 312 *paths: (list of strings) list of filenames to pass to `git add`. | 332 *paths: (list of strings) list of filenames to pass to `git add`. |
| 313 """ | 333 """ |
| 314 self._file_list.extend(paths) | 334 self._file_list.extend(paths) |
| 315 | 335 |
| 336 def set_message(self, message): | |
| 337 """Change the message.""" | |
| 338 self._message = message | |
| 339 | |
| 316 def __enter__(self): | 340 def __enter__(self): |
| 317 git = self._config.git | 341 git = self._config.git |
| 318 def branch_exists(branch): | 342 def branch_exists(branch): |
| 319 """Return true iff branch exists.""" | 343 """Return true iff branch exists.""" |
| 320 return 0 == subprocess.call( | 344 return 0 == subprocess.call( |
| 321 [git, 'show-ref', '--quiet', branch]) | 345 [git, 'show-ref', '--quiet', branch]) |
| 322 def has_diff(): | 346 def has_diff(): |
| 323 """Return true iff repository has uncommited changes.""" | 347 """Return true iff repository has uncommited changes.""" |
| 324 return bool(subprocess.call([git, 'diff', '--quiet', 'HEAD'])) | 348 return bool(subprocess.call([git, 'diff', '--quiet', 'HEAD'])) |
| 349 def get_svn_revision(): | |
| 350 """Works in both git and git-svn. returns a string""" | |
| 351 last_log_message = subprocess.check_output( | |
| 352 [git, 'log', '-n', '1', '--format=format:%B']) | |
| 353 svn_format = ( | |
| 354 '(git-svn-id: svn://svn.chromium.org/chrome/trunk/src@|' | |
| 355 'SVN changes up to revision )([0-9]+)') | |
| 356 search = re.search(svn_format, last_log_message) | |
| 357 if not search: | |
| 358 raise DepsRollError( | |
| 359 'Revision number missing from Chromium origin/master.') | |
| 360 return search.group(2) | |
| 325 self._stash = has_diff() | 361 self._stash = has_diff() |
| 326 if self._stash: | 362 if self._stash: |
| 327 check_call([git, 'stash', 'save']) | 363 check_call([git, 'stash', 'save']) |
| 328 try: | 364 try: |
| 329 self._original_branch = strip_output( | 365 self._original_branch = strip_output( |
| 330 [git, 'symbolic-ref', '--short', 'HEAD']) | 366 [git, 'symbolic-ref', '--short', 'HEAD']) |
| 331 except (subprocess.CalledProcessError,): | 367 except (subprocess.CalledProcessError,): |
| 332 self._original_branch = strip_output( | 368 self._original_branch = strip_output( |
| 333 [git, 'rev-parse', 'HEAD']) | 369 [git, 'rev-parse', 'HEAD']) |
| 334 | 370 |
| 335 if not self._branch_name: | 371 if not self._branch_name: |
| 336 self._branch_name = self._config.default_branch_name | 372 self._branch_name = self._config.default_branch_name |
| 337 | 373 |
| 338 if branch_exists(self._branch_name): | 374 if branch_exists(self._branch_name): |
| 339 check_call([git, 'checkout', '-q', 'master']) | 375 check_call([git, 'checkout', '-q', 'master']) |
| 340 check_call([git, 'branch', '-q', '-D', self._branch_name]) | 376 check_call([git, 'branch', '-q', '-D', self._branch_name]) |
| 341 | 377 |
| 342 check_call( | 378 check_call( |
| 343 [git, 'checkout', '-q', '-b', | 379 [git, 'checkout', '-q', '-b', |
| 344 self._branch_name, 'origin/master']) | 380 self._branch_name, 'origin/master']) |
| 345 | 381 self._svn_info = get_svn_revision() |
| 346 svn_info = subprocess.check_output(['git', 'svn', 'info']) | |
| 347 svn_info_search = re.search(r'Last Changed Rev: ([0-9]+)\W', svn_info) | |
| 348 assert svn_info_search | |
| 349 self._svn_info = svn_info_search.group(1) | |
| 350 | 382 |
| 351 def __exit__(self, etype, value, traceback): | 383 def __exit__(self, etype, value, traceback): |
| 352 # pylint: disable=I0011,R0912 | 384 # pylint: disable=I0011,R0912 |
| 353 git = self._config.git | 385 git = self._config.git |
| 354 def quiet_check_call(*args, **kwargs): | 386 def quiet_check_call(*args, **kwargs): |
| 355 """Call check_call, but pipe output to devnull.""" | 387 """Call check_call, but pipe output to devnull.""" |
| 356 with open(os.devnull, 'w') as devnull: | 388 with open(os.devnull, 'w') as devnull: |
| 357 check_call(*args, stdout=devnull, **kwargs) | 389 check_call(*args, stdout=devnull, **kwargs) |
| 358 | 390 |
| 359 for filename in self._file_list: | 391 for filename in self._file_list: |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 450 revision: (int) Skia SVN revision. | 482 revision: (int) Skia SVN revision. |
| 451 git_hash: (string) Skia Git hash. | 483 git_hash: (string) Skia Git hash. |
| 452 | 484 |
| 453 Returns: | 485 Returns: |
| 454 a tuple containing textual description of the two issues. | 486 a tuple containing textual description of the two issues. |
| 455 | 487 |
| 456 Raises: | 488 Raises: |
| 457 OSError: failed to execute git or git-cl. | 489 OSError: failed to execute git or git-cl. |
| 458 subprocess.CalledProcessError: git returned unexpected status. | 490 subprocess.CalledProcessError: git returned unexpected status. |
| 459 """ | 491 """ |
| 492 def search_within_file(path, pattern, default=None): | |
|
borenet
2014/01/07 21:31:12
I think this function could live outside of roll_d
hal.canary
2014/01/08 18:57:38
Done.
It's in a utility class. If you want to ma
| |
| 493 """Search for regular expression in a file. | |
| 494 | |
| 495 Opens a file for reading and searches line by line for a match to | |
| 496 the regex and returns the parenthesized group named return for the | |
| 497 first match. | |
|
borenet
2014/01/07 21:31:12
Please document that the pattern can't contain new
hal.canary
2014/01/08 18:57:38
Done.
| |
| 498 | |
| 499 For example: | |
| 500 pattern = '^root(:[^:]*){4}:(?P<return>[^:]*)' | |
| 501 search_within_file('/etc/passwd', pattern) | |
| 502 should return root's home directory (/root on my system). | |
| 503 | |
| 504 Args: | |
| 505 path: (string) filename | |
| 506 pattern: (string) to be passed to re.compile | |
| 507 default: what to return if no match | |
| 508 | |
| 509 Returns: | |
| 510 A string or whatever default is | |
| 511 """ | |
| 512 with open(path, 'r') as input_stream: | |
| 513 pattern_object = re.compile(pattern) | |
| 514 for line in input_stream: | |
| 515 match = pattern_object.search(line) | |
| 516 if match: | |
| 517 return match.group('return') | |
| 518 return default | |
| 519 | |
| 520 def search_within_string(input_string, pattern, default=None): | |
|
borenet
2014/01/07 21:31:12
Same comment about this function: I think it could
hal.canary
2014/01/08 18:57:38
Done.
| |
| 521 """Search for regular expression in a string. | |
| 522 | |
| 523 Args: | |
| 524 input_string: (string) to be searched | |
| 525 pattern: (string) to be passed to re.compile | |
| 526 default: what to return if no match | |
| 527 | |
| 528 Returns: | |
| 529 A string or whatever default is | |
| 530 """ | |
| 531 match = re.search(pattern, input_string) | |
| 532 return match.group('return') if match else default | |
| 533 | |
| 460 git = config.git | 534 git = config.git |
| 461 cwd = os.getcwd() | 535 with ChangeDir(config.chromium_path): |
| 462 os.chdir(config.chromium_path) | |
| 463 try: | |
| 464 check_call([git, 'fetch', '-q', 'origin']) | 536 check_call([git, 'fetch', '-q', 'origin']) |
| 465 master_hash = strip_output( | 537 master_hash = strip_output( |
| 466 [git, 'show-ref', 'origin/master', '--hash']) | 538 [git, 'show-ref', 'origin/master', '--hash']) |
| 467 | 539 |
| 540 branch = None | |
| 541 | |
| 468 # master_hash[8] gives each whitespace CL a unique name. | 542 # master_hash[8] gives each whitespace CL a unique name. |
| 469 message = ('whitespace change %s\n\nThis CL was created by' | 543 message = ('whitespace change %s\n\nThis CL was created by' |
| 470 ' Skia\'s roll_deps.py script.\n') % master_hash[:8] | 544 ' Skia\'s roll_deps.py script.\n') % master_hash[:8] |
| 471 branch = branch_name(message) if config.save_branches else None | 545 if config.save_branches: |
| 546 branch = 'control_%s' % master_hash[:8] | |
|
borenet
2014/01/07 21:31:12
Maybe we should have a short_hash(hash) function?
hal.canary
2014/01/08 18:57:38
In Python, string[:8] is clear enough.
| |
| 472 | 547 |
| 473 codereview = GitBranchCLUpload(config, message, branch) | 548 codereview = GitBranchCLUpload(config, message, branch) |
| 474 with codereview: | 549 with codereview: |
| 475 with open('build/whitespace_file.txt', 'a') as output_stream: | 550 with open('build/whitespace_file.txt', 'a') as output_stream: |
| 476 output_stream.write('\nCONTROL\n') | 551 output_stream.write('\nCONTROL\n') |
| 477 codereview.stage_for_commit('build/whitespace_file.txt') | 552 codereview.stage_for_commit('build/whitespace_file.txt') |
| 478 whitespace_cl = codereview.issue | 553 whitespace_cl = codereview.issue |
| 479 if branch: | 554 if branch: |
| 480 whitespace_cl = '%s\n branch: %s' % (whitespace_cl, branch) | 555 whitespace_cl = '%s\n branch: %s' % (whitespace_cl, branch) |
| 481 control_url_match = re.search('https?://[^) ]+', codereview.issue) | 556 |
| 482 if control_url_match: | 557 control_url = search_within_string( |
| 483 message = ('roll skia DEPS to %d\n\nThis CL was created by' | 558 codereview.issue, '(?P<return>https?://[^) ]+)', '?') |
| 484 ' Skia\'s roll_deps.py script.\n\ncontrol: %s' | 559 |
| 485 % (revision, control_url_match.group(0))) | 560 if config.save_branches: |
| 486 else: | 561 branch = 'roll_%d_%s' % (revision, master_hash[:8]) |
|
borenet
2014/01/07 21:39:34
I just found this: http://stackoverflow.com/a/6065
hal.canary
2014/01/08 18:57:38
Good idea, but defering to robert.
| |
| 487 message = ('roll skia DEPS to %d\n\nThis CL was created by' | 562 codereview = GitBranchCLUpload(config, '?', branch) |
| 488 ' Skia\'s roll_deps.py script.') % revision | |
| 489 branch = branch_name(message) if config.save_branches else None | |
| 490 codereview = GitBranchCLUpload(config, message, branch) | |
| 491 with codereview: | 563 with codereview: |
| 564 old_revision = search_within_file( | |
| 565 'DEPS', '"skia_revision": "(?P<return>[0-9]+)",', '?') | |
| 566 assert revision != int(old_revision) | |
|
borenet
2014/01/07 21:31:12
Can we move the whitespace CL uploading to after t
hal.canary
2014/01/08 18:57:38
I moved that assert up. And turned it into a chec
| |
| 567 codereview.set_message( | |
| 568 'roll skia DEPS to %d\n\nold revision:%s\n' | |
| 569 'new revision:%d\nThis CL was created by' | |
| 570 ' Skia\'s roll_deps.py script.\n\ncontrol: %s' | |
| 571 % (revision, old_revision, revision, control_url)) | |
| 492 change_skia_deps(revision, git_hash, 'DEPS') | 572 change_skia_deps(revision, git_hash, 'DEPS') |
| 493 codereview.stage_for_commit('DEPS') | 573 codereview.stage_for_commit('DEPS') |
| 494 deps_cl = codereview.issue | 574 deps_cl = codereview.issue |
| 495 if branch: | 575 if branch: |
| 496 deps_cl = '%s\n branch: %s' % (deps_cl, branch) | 576 deps_cl = '%s\n branch: %s' % (deps_cl, branch) |
| 497 | 577 |
| 498 return deps_cl, whitespace_cl | 578 return deps_cl, whitespace_cl |
| 499 finally: | |
| 500 os.chdir(cwd) | |
| 501 | 579 |
| 502 | 580 |
| 503 def find_hash_and_roll_deps(config, revision): | 581 def find_hash_and_roll_deps(config, revision): |
| 504 """Call find_hash_from_revision() and roll_deps(). | 582 """Call find_hash_from_revision() and roll_deps(). |
| 505 | 583 |
| 506 The calls to git will be verbose on standard output. After a | 584 The calls to git will be verbose on standard output. After a |
| 507 successful upload of both issues, print links to the new | 585 successful upload of both issues, print links to the new |
| 508 codereview issues. | 586 codereview issues. |
| 509 | 587 |
| 510 Args: | 588 Args: |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 543 if not test_git_executable(options.git_path): | 621 if not test_git_executable(options.git_path): |
| 544 option_parser.error('Invalid git executable.') | 622 option_parser.error('Invalid git executable.') |
| 545 | 623 |
| 546 config = DepsRollConfig(options) | 624 config = DepsRollConfig(options) |
| 547 find_hash_and_roll_deps(config, options.revision) | 625 find_hash_and_roll_deps(config, options.revision) |
| 548 | 626 |
| 549 | 627 |
| 550 if __name__ == '__main__': | 628 if __name__ == '__main__': |
| 551 main(sys.argv[1:]) | 629 main(sys.argv[1:]) |
| 552 | 630 |
| OLD | NEW |