Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 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 | 6 |
| 7 """Automates creation and management of DEPS roll CLs. | 7 """Automates creation and management of DEPS roll CLs. |
| 8 | 8 |
| 9 This script is designed to be run in a loop (eg. with auto_roll_wrapper.sh) or | 9 This script is designed to be run in a loop (eg. with auto_roll_wrapper.sh) or |
| 10 on a timer. It may take one of several actions, depending on the state of | 10 on a timer. It may take one of several actions, depending on the state of |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 141 ROLL_DESCRIPTION_REGEXP = ROLL_DESCRIPTION_STR % { | 141 ROLL_DESCRIPTION_REGEXP = ROLL_DESCRIPTION_STR % { |
| 142 'project': '%(project)s', | 142 'project': '%(project)s', |
| 143 'from_revision': r'(?P<from_revision>[0-9a-fA-F]{2,40})', | 143 'from_revision': r'(?P<from_revision>[0-9a-fA-F]{2,40})', |
| 144 'to_revision': r'(?P<to_revision>[0-9a-fA-F]{2,40})' | 144 'to_revision': r'(?P<to_revision>[0-9a-fA-F]{2,40})' |
| 145 } | 145 } |
| 146 | 146 |
| 147 # FIXME: These are taken from gardeningserver.py and should be shared. | 147 # FIXME: These are taken from gardeningserver.py and should be shared. |
| 148 CHROMIUM_SVN_DEPS_URL = 'http://src.chromium.org/chrome/trunk/src/DEPS' | 148 CHROMIUM_SVN_DEPS_URL = 'http://src.chromium.org/chrome/trunk/src/DEPS' |
| 149 # 'webkit_revision': '149598', | 149 # 'webkit_revision': '149598', |
| 150 REVISION_REGEXP = ( | 150 REVISION_REGEXP = ( |
| 151 r'^ [\'"]%s_revision[\'"]: [\'"](?P<revision>[0-9a-fA-F]{2,40})[\'"],') | 151 r'^ [\'"]%s_revision[\'"]: [\'"](?P<revision>[0-9a-fA-F]{2,40})[\'"],' |
| 152 r'( # from svn revision (?P<svn_revision>\d+))?') | |
| 152 | 153 |
| 153 ROLL_BOT_INSTRUCTIONS = textwrap.dedent( | 154 ROLL_BOT_INSTRUCTIONS = textwrap.dedent( |
| 154 '''This roll was created by the Blink AutoRollBot. | 155 '''This roll was created by the Blink AutoRollBot. |
| 155 http://www.chromium.org/blink/blinkrollbot''') | 156 http://www.chromium.org/blink/blinkrollbot''') |
| 156 | 157 |
| 157 PLEASE_RESUME_NAG = textwrap.dedent(''' | 158 PLEASE_RESUME_NAG = textwrap.dedent(''' |
| 158 Rollbot was stopped by the presence of 'STOP' in an earlier comment. | 159 Rollbot was stopped by the presence of 'STOP' in an earlier comment. |
| 159 The last update to this issue was over %(stop_nag_timeout)s hours ago. | 160 The last update to this issue was over %(stop_nag_timeout)s hours ago. |
| 160 Please close this issue as soon as possible to allow the bot to continue. | 161 Please close this issue as soon as possible to allow the bot to continue. |
| 161 | 162 |
| 162 Please email (%(admin)s) if the Rollbot is causing trouble. | 163 Please email (%(admin)s) if the Rollbot is causing trouble. |
| 163 ''' % {'admin': ADMIN_EMAIL, 'stop_nag_timeout': STOP_NAG_TIME_LIMIT}) | 164 ''' % {'admin': ADMIN_EMAIL, 'stop_nag_timeout': STOP_NAG_TIME_LIMIT}) |
| 164 | 165 |
| 165 def __init__(self, project, author, path_to_chrome): | 166 def __init__(self, project, author, path_to_chrome): |
| 166 self._author = author | 167 self._author = author |
| 167 self._project = project | 168 self._project = project |
| 168 self._path_to_chrome = path_to_chrome | 169 self._path_to_chrome = path_to_chrome |
| 169 self._rietveld = rietveld.Rietveld( | 170 self._rietveld = rietveld.Rietveld( |
| 170 self.RIETVELD_URL, self._author, None) | 171 self.RIETVELD_URL, self._author, None) |
| 171 self._cached_last_roll_revision = None | 172 self._cached_last_roll_revision = None |
| 173 self._cached_deps_contents = None | |
| 172 | 174 |
| 173 project_config = PROJECT_CONFIGS.get(self._project, { | 175 project_config = PROJECT_CONFIGS.get(self._project, { |
| 174 'path_to_project': os.path.join('third_party', self._project), | 176 'path_to_project': os.path.join('third_party', self._project), |
| 175 'revision_link_fn': lambda before_rev, after_ref: '', | 177 'revision_link_fn': lambda before_rev, after_ref: '', |
| 176 }) | 178 }) |
| 177 self._project_alias = project_config.get('project_alias', self._project) | 179 self._project_alias = project_config.get('project_alias', self._project) |
| 178 self._path_to_project = project_config['path_to_project'] | 180 self._path_to_project = project_config['path_to_project'] |
| 179 self._get_revision_link = project_config['revision_link_fn'] | 181 self._get_revision_link = project_config['revision_link_fn'] |
| 180 self._get_extra_emails = project_config.get('extra_emails_fn', lambda: []) | 182 self._get_extra_emails = project_config.get('extra_emails_fn', lambda: []) |
| 181 self._git_mode = project_config.get('git_mode', False) | 183 self._git_mode = project_config.get('git_mode', False) |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 print 'Closing %s with message: \'%s\'' % ( | 223 print 'Closing %s with message: \'%s\'' % ( |
| 222 self._url_for_issue(issue_number), message) | 224 self._url_for_issue(issue_number), message) |
| 223 if message: | 225 if message: |
| 224 self._rietveld.add_comment(issue_number, message) | 226 self._rietveld.add_comment(issue_number, message) |
| 225 self._rietveld.close_issue(issue_number) | 227 self._rietveld.close_issue(issue_number) |
| 226 | 228 |
| 227 def _path_from_chromium_root(self, *components): | 229 def _path_from_chromium_root(self, *components): |
| 228 assert os.pardir not in components | 230 assert os.pardir not in components |
| 229 return os.path.join(self._path_to_chrome, *components) | 231 return os.path.join(self._path_to_chrome, *components) |
| 230 | 232 |
| 233 def _do_git_fetch(self, git_dir): | |
| 234 subprocess2.check_call(['git', '--git-dir', git_dir, 'fetch']) | |
| 235 | |
| 231 def _last_roll_revision(self): | 236 def _last_roll_revision(self): |
| 232 """Returns the revision of the last roll. | 237 """Returns the revision of the last roll. |
| 233 | 238 |
| 234 Returns: | 239 Returns: |
| 235 revision of the last roll; either a 40-character Git commit hash or an | 240 revision of the last roll; either a 40-character Git commit hash or an |
| 236 SVN revision number. | 241 SVN revision number. |
| 237 """ | 242 """ |
| 238 if not self._cached_last_roll_revision: | 243 if not self._cached_last_roll_revision: |
| 239 subprocess2.check_call(['git', '--git-dir', self._chromium_git_dir, | 244 self._cached_last_roll_revision = self._last_roll_revision_helper( |
| 240 'fetch']) | 245 'revision') |
|
szager1
2014/09/11 00:52:04
nit: indentation should be four characters in from
skobes
2014/09/11 01:32:42
Done.
| |
| 241 git_show_cmd = ['git', '--git-dir', self._chromium_git_dir, 'show', | |
| 242 'origin/master:DEPS'] | |
| 243 deps_contents = subprocess2.check_output(git_show_cmd) | |
| 244 pattern = self.REVISION_REGEXP % self._project_alias | |
| 245 match = re.search(pattern, deps_contents, re.MULTILINE) | |
| 246 self._cached_last_roll_revision = match.group('revision') | |
| 247 if self._git_mode: | 246 if self._git_mode: |
| 248 assert len(self._cached_last_roll_revision) == 40 | 247 assert len(self._cached_last_roll_revision) == 40 |
| 249 return self._cached_last_roll_revision | 248 return self._cached_last_roll_revision |
| 250 | 249 |
| 250 def _last_roll_revision_svn(self): | |
| 251 return self._last_roll_revision_helper('svn_revision') | |
| 252 | |
| 253 def _last_roll_revision_helper(self, match_group): | |
| 254 if not self._cached_deps_contents: | |
| 255 git_show_cmd = ['git', '--git-dir', self._chromium_git_dir, 'show', | |
| 256 'origin/master:DEPS'] | |
| 257 self._cached_deps_contents = subprocess2.check_output(git_show_cmd) | |
| 258 | |
| 259 pattern = self.REVISION_REGEXP % self._project_alias | |
| 260 match = re.search(pattern, self._cached_deps_contents, re.MULTILINE) | |
| 261 return match.group(match_group) | |
| 262 | |
| 251 def _current_revision(self): | 263 def _current_revision(self): |
| 252 subprocess2.check_call(['git', '--git-dir', self._project_git_dir, | |
| 253 'fetch']) | |
| 254 if self._git_mode: | 264 if self._git_mode: |
| 255 git_revparse_cmd = ['git', '--git-dir', self._project_git_dir, | 265 git_revparse_cmd = ['git', '--git-dir', self._project_git_dir, |
| 256 'rev-parse', 'origin/master'] | 266 'rev-parse', 'origin/master'] |
| 257 return subprocess2.check_output(git_revparse_cmd).rstrip() | 267 return subprocess2.check_output(git_revparse_cmd).rstrip() |
| 258 else: | 268 else: |
| 259 git_show_cmd = ['git', '--git-dir', self._project_git_dir, 'show', '-s', | 269 return self._current_revision_svn() |
| 260 'origin/master'] | 270 |
| 261 git_log = subprocess2.check_output(git_show_cmd) | 271 def _current_revision_svn(self): |
| 262 match = re.search('^\s*git-svn-id:.*@(?P<svn_revision>\d+)\ ', | 272 git_show_cmd = ['git', '--git-dir', self._project_git_dir, 'show', '-s', |
| 263 git_log, re.MULTILINE) | 273 'origin/master'] |
| 264 if match: | 274 git_log = subprocess2.check_output(git_show_cmd) |
| 265 return match.group('svn_revision') | 275 match = re.search('^\s*git-svn-id:.*@(?P<svn_revision>\d+)\ ', |
| 266 else: | 276 git_log, re.MULTILINE) |
| 267 raise AutoRollException( | 277 if match: |
| 268 'Could not determine the current SVN revision.') | 278 return match.group('svn_revision') |
| 279 else: | |
| 280 return None | |
| 269 | 281 |
| 270 def _emails_to_cc_on_rolls(self): | 282 def _emails_to_cc_on_rolls(self): |
| 271 return _filter_emails(self._get_extra_emails()) | 283 return _filter_emails(self._get_extra_emails()) |
| 272 | 284 |
| 273 def _start_roll(self, new_roll_revision, commit_msg): | 285 def _start_roll(self, new_roll_revision, commit_msg): |
| 274 roll_branch = '%s_roll' % self._project | 286 roll_branch = '%s_roll' % self._project |
| 275 cwd_kwargs = {'cwd': self._path_to_chrome} | 287 cwd_kwargs = {'cwd': self._path_to_chrome} |
| 276 subprocess2.check_call(['git', 'clean', '-d', '-f'], **cwd_kwargs) | 288 subprocess2.check_call(['git', 'clean', '-d', '-f'], **cwd_kwargs) |
| 277 subprocess2.call(['git', 'rebase', '--abort'], **cwd_kwargs) | 289 subprocess2.call(['git', 'rebase', '--abort'], **cwd_kwargs) |
| 278 subprocess2.call(['git', 'branch', '-D', roll_branch], **cwd_kwargs) | 290 subprocess2.call(['git', 'branch', '-D', roll_branch], **cwd_kwargs) |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 386 issue = None | 398 issue = None |
| 387 | 399 |
| 388 if issue: | 400 if issue: |
| 389 if self._rollbot_should_stop(issue): | 401 if self._rollbot_should_stop(issue): |
| 390 return 1 | 402 return 1 |
| 391 if not self._maybe_close_active_roll(issue): | 403 if not self._maybe_close_active_roll(issue): |
| 392 print '%s is still active, nothing to do.' % \ | 404 print '%s is still active, nothing to do.' % \ |
| 393 self._url_for_issue(issue_number) | 405 self._url_for_issue(issue_number) |
| 394 return 0 | 406 return 0 |
| 395 | 407 |
| 408 self._do_git_fetch(self._chromium_git_dir) | |
| 409 self._do_git_fetch(self._project_git_dir) | |
| 410 | |
| 396 last_roll_revision = self._last_roll_revision() | 411 last_roll_revision = self._last_roll_revision() |
| 397 new_roll_revision = self._current_revision() | 412 new_roll_revision = self._current_revision() |
| 413 | |
| 414 if not new_roll_revision: | |
| 415 raise AutoRollException( | |
| 416 'Could not determine the current revision.') | |
| 417 | |
| 418 if self._git_mode: | |
| 419 last_roll_revision_svn = self._last_roll_revision_svn() | |
| 420 new_roll_revision_svn = self._current_revision_svn() | |
| 421 else: | |
| 422 last_roll_revision_svn = None | |
| 423 new_roll_revision_svn = None | |
| 424 | |
| 398 self._compare_revisions(last_roll_revision, new_roll_revision) | 425 self._compare_revisions(last_roll_revision, new_roll_revision) |
| 399 | 426 |
| 400 display_from_rev = ( | 427 display_from_rev = ( |
| 401 self._short_rev(last_roll_revision) if self._git_mode | 428 self._short_rev(last_roll_revision) if self._git_mode |
| 402 else last_roll_revision) | 429 else last_roll_revision) |
| 403 display_to_rev = ( | 430 display_to_rev = ( |
| 404 self._short_rev(new_roll_revision) if self._git_mode | 431 self._short_rev(new_roll_revision) if self._git_mode |
| 405 else new_roll_revision) | 432 else new_roll_revision) |
| 406 commit_msg = self.ROLL_DESCRIPTION_STR % { | 433 commit_msg = self.ROLL_DESCRIPTION_STR % { |
| 407 'project': self._project.title(), | 434 'project': self._project.title(), |
| 408 'from_revision': display_from_rev, | 435 'from_revision': display_from_rev, |
| 409 'to_revision': display_to_rev, | 436 'to_revision': display_to_rev, |
| 410 } | 437 } |
| 438 | |
| 439 if last_roll_revision_svn and new_roll_revision_svn: | |
| 440 commit_msg += ' (svn %s:%s)' % ( | |
| 441 last_roll_revision_svn, new_roll_revision_svn) | |
| 442 | |
| 411 revlink = self._get_revision_link(last_roll_revision, new_roll_revision) | 443 revlink = self._get_revision_link(last_roll_revision, new_roll_revision) |
| 412 if revlink: | 444 if revlink: |
| 413 commit_msg += '\n\n' + revlink | 445 commit_msg += '\n\n' + revlink |
| 414 | 446 |
| 415 if self._cq_extra_trybots: | 447 if self._cq_extra_trybots: |
| 416 commit_msg += '\n\n' + CQ_EXTRA_TRYBOTS + ','.join(self._cq_extra_trybots) | 448 commit_msg += '\n\n' + CQ_EXTRA_TRYBOTS + ','.join(self._cq_extra_trybots) |
| 417 | 449 |
| 418 self._start_roll(new_roll_revision, commit_msg) | 450 self._start_roll(new_roll_revision, commit_msg) |
| 419 return 0 | 451 return 0 |
| 420 | 452 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 436 _, args = parser.parse_args() | 468 _, args = parser.parse_args() |
| 437 if len(args) != 3: | 469 if len(args) != 3: |
| 438 parser.print_usage() | 470 parser.print_usage() |
| 439 return 1 | 471 return 1 |
| 440 | 472 |
| 441 AutoRoller(*args).main() | 473 AutoRoller(*args).main() |
| 442 | 474 |
| 443 | 475 |
| 444 if __name__ == '__main__': | 476 if __name__ == '__main__': |
| 445 sys.exit(main()) | 477 sys.exit(main()) |
| OLD | NEW |