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 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 | 97 |
98 | 98 |
99 def _current_gardener_emails(): | 99 def _current_gardener_emails(): |
100 return _emails_from_url(BLINK_SHERIFF_URL) | 100 return _emails_from_url(BLINK_SHERIFF_URL) |
101 | 101 |
102 | 102 |
103 def _current_sheriff_emails(): | 103 def _current_sheriff_emails(): |
104 return _emails_from_url(CHROMIUM_SHERIFF_URL) | 104 return _emails_from_url(CHROMIUM_SHERIFF_URL) |
105 | 105 |
106 | 106 |
| 107 def _do_git_fetch(git_dir): |
| 108 subprocess2.check_call(['git', '--git-dir', git_dir, 'fetch']) |
| 109 |
| 110 |
107 PROJECT_CONFIGS = { | 111 PROJECT_CONFIGS = { |
108 'blink': { | 112 'blink': { |
109 'extra_emails_fn': _current_gardener_emails, | 113 'extra_emails_fn': _current_gardener_emails, |
110 'git_mode': True, | 114 'git_mode': True, |
111 'path_to_project': os.path.join('third_party', 'WebKit'), | 115 'path_to_project': os.path.join('third_party', 'WebKit'), |
112 'project_alias': 'webkit', | 116 'project_alias': 'webkit', |
113 'revision_link_fn': lambda before_rev, after_rev: ( | 117 'revision_link_fn': lambda before_rev, after_rev: ( |
114 'https://chromium.googlesource.com/chromium/blink/+log/%s..%s' % ( | 118 'https://chromium.googlesource.com/chromium/blink/+log/%s..%s' % ( |
115 before_rev, after_rev)), | 119 before_rev, after_rev)), |
116 }, | 120 }, |
(...skipping 24 matching lines...) Expand all Loading... |
141 ROLL_DESCRIPTION_REGEXP = ROLL_DESCRIPTION_STR % { | 145 ROLL_DESCRIPTION_REGEXP = ROLL_DESCRIPTION_STR % { |
142 'project': '%(project)s', | 146 'project': '%(project)s', |
143 'from_revision': r'(?P<from_revision>[0-9a-fA-F]{2,40})', | 147 '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})' | 148 'to_revision': r'(?P<to_revision>[0-9a-fA-F]{2,40})' |
145 } | 149 } |
146 | 150 |
147 # FIXME: These are taken from gardeningserver.py and should be shared. | 151 # FIXME: These are taken from gardeningserver.py and should be shared. |
148 CHROMIUM_SVN_DEPS_URL = 'http://src.chromium.org/chrome/trunk/src/DEPS' | 152 CHROMIUM_SVN_DEPS_URL = 'http://src.chromium.org/chrome/trunk/src/DEPS' |
149 # 'webkit_revision': '149598', | 153 # 'webkit_revision': '149598', |
150 REVISION_REGEXP = ( | 154 REVISION_REGEXP = ( |
151 r'^ [\'"]%s_revision[\'"]: [\'"](?P<revision>[0-9a-fA-F]{2,40})[\'"],') | 155 r'^ [\'"]%s_revision[\'"]: [\'"](?P<revision>[0-9a-fA-F]{2,40})[\'"],' |
| 156 r'( # from svn revision (?P<svn_revision>\d+))?') |
152 | 157 |
153 ROLL_BOT_INSTRUCTIONS = textwrap.dedent( | 158 ROLL_BOT_INSTRUCTIONS = textwrap.dedent( |
154 '''This roll was created by the Blink AutoRollBot. | 159 '''This roll was created by the Blink AutoRollBot. |
155 http://www.chromium.org/blink/blinkrollbot''') | 160 http://www.chromium.org/blink/blinkrollbot''') |
156 | 161 |
157 PLEASE_RESUME_NAG = textwrap.dedent(''' | 162 PLEASE_RESUME_NAG = textwrap.dedent(''' |
158 Rollbot was stopped by the presence of 'STOP' in an earlier comment. | 163 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. | 164 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. | 165 Please close this issue as soon as possible to allow the bot to continue. |
161 | 166 |
162 Please email (%(admin)s) if the Rollbot is causing trouble. | 167 Please email (%(admin)s) if the Rollbot is causing trouble. |
163 ''' % {'admin': ADMIN_EMAIL, 'stop_nag_timeout': STOP_NAG_TIME_LIMIT}) | 168 ''' % {'admin': ADMIN_EMAIL, 'stop_nag_timeout': STOP_NAG_TIME_LIMIT}) |
164 | 169 |
165 def __init__(self, project, author, path_to_chrome): | 170 def __init__(self, project, author, path_to_chrome): |
166 self._author = author | 171 self._author = author |
167 self._project = project | 172 self._project = project |
168 self._path_to_chrome = path_to_chrome | 173 self._path_to_chrome = path_to_chrome |
169 self._rietveld = rietveld.Rietveld( | 174 self._rietveld = rietveld.Rietveld( |
170 self.RIETVELD_URL, self._author, None) | 175 self.RIETVELD_URL, self._author, None) |
171 self._cached_last_roll_revision = None | 176 self._cached_last_roll_revision = None |
| 177 self._cached_deps_contents = None |
172 | 178 |
173 project_config = PROJECT_CONFIGS.get(self._project, { | 179 project_config = PROJECT_CONFIGS.get(self._project, { |
174 'path_to_project': os.path.join('third_party', self._project), | 180 'path_to_project': os.path.join('third_party', self._project), |
175 'revision_link_fn': lambda before_rev, after_ref: '', | 181 'revision_link_fn': lambda before_rev, after_ref: '', |
176 }) | 182 }) |
177 self._project_alias = project_config.get('project_alias', self._project) | 183 self._project_alias = project_config.get('project_alias', self._project) |
178 self._path_to_project = project_config['path_to_project'] | 184 self._path_to_project = project_config['path_to_project'] |
179 self._get_revision_link = project_config['revision_link_fn'] | 185 self._get_revision_link = project_config['revision_link_fn'] |
180 self._get_extra_emails = project_config.get('extra_emails_fn', lambda: []) | 186 self._get_extra_emails = project_config.get('extra_emails_fn', lambda: []) |
181 self._git_mode = project_config.get('git_mode', False) | 187 self._git_mode = project_config.get('git_mode', False) |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 return os.path.join(self._path_to_chrome, *components) | 235 return os.path.join(self._path_to_chrome, *components) |
230 | 236 |
231 def _last_roll_revision(self): | 237 def _last_roll_revision(self): |
232 """Returns the revision of the last roll. | 238 """Returns the revision of the last roll. |
233 | 239 |
234 Returns: | 240 Returns: |
235 revision of the last roll; either a 40-character Git commit hash or an | 241 revision of the last roll; either a 40-character Git commit hash or an |
236 SVN revision number. | 242 SVN revision number. |
237 """ | 243 """ |
238 if not self._cached_last_roll_revision: | 244 if not self._cached_last_roll_revision: |
239 subprocess2.check_call(['git', '--git-dir', self._chromium_git_dir, | 245 self._cached_last_roll_revision = self._last_roll_revision_helper( |
240 'fetch']) | 246 'revision') |
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: | 247 if self._git_mode: |
248 assert len(self._cached_last_roll_revision) == 40 | 248 assert len(self._cached_last_roll_revision) == 40 |
249 return self._cached_last_roll_revision | 249 return self._cached_last_roll_revision |
250 | 250 |
| 251 def _last_roll_revision_svn(self): |
| 252 return self._last_roll_revision_helper('svn_revision') |
| 253 |
| 254 def _last_roll_revision_helper(self, match_group): |
| 255 if not self._cached_deps_contents: |
| 256 git_show_cmd = ['git', '--git-dir', self._chromium_git_dir, 'show', |
| 257 'origin/master:DEPS'] |
| 258 self._cached_deps_contents = subprocess2.check_output(git_show_cmd) |
| 259 |
| 260 pattern = self.REVISION_REGEXP % self._project_alias |
| 261 match = re.search(pattern, self._cached_deps_contents, re.MULTILINE) |
| 262 return match.group(match_group) |
| 263 |
251 def _current_revision(self): | 264 def _current_revision(self): |
252 subprocess2.check_call(['git', '--git-dir', self._project_git_dir, | |
253 'fetch']) | |
254 if self._git_mode: | 265 if self._git_mode: |
255 git_revparse_cmd = ['git', '--git-dir', self._project_git_dir, | 266 git_revparse_cmd = ['git', '--git-dir', self._project_git_dir, |
256 'rev-parse', 'origin/master'] | 267 'rev-parse', 'origin/master'] |
257 return subprocess2.check_output(git_revparse_cmd).rstrip() | 268 return subprocess2.check_output(git_revparse_cmd).rstrip() |
258 else: | 269 else: |
259 git_show_cmd = ['git', '--git-dir', self._project_git_dir, 'show', '-s', | 270 return self._current_revision_svn() |
260 'origin/master'] | 271 |
261 git_log = subprocess2.check_output(git_show_cmd) | 272 def _current_revision_svn(self): |
262 match = re.search('^\s*git-svn-id:.*@(?P<svn_revision>\d+)\ ', | 273 git_show_cmd = ['git', '--git-dir', self._project_git_dir, 'show', '-s', |
263 git_log, re.MULTILINE) | 274 'origin/master'] |
264 if match: | 275 git_log = subprocess2.check_output(git_show_cmd) |
265 return match.group('svn_revision') | 276 match = re.search('^\s*git-svn-id:.*@(?P<svn_revision>\d+)\ ', |
266 else: | 277 git_log, re.MULTILINE) |
267 raise AutoRollException( | 278 if match: |
268 'Could not determine the current SVN revision.') | 279 return match.group('svn_revision') |
| 280 else: |
| 281 return None |
269 | 282 |
270 def _emails_to_cc_on_rolls(self): | 283 def _emails_to_cc_on_rolls(self): |
271 return _filter_emails(self._get_extra_emails()) | 284 return _filter_emails(self._get_extra_emails()) |
272 | 285 |
273 def _start_roll(self, new_roll_revision, commit_msg): | 286 def _start_roll(self, new_roll_revision, commit_msg): |
274 roll_branch = '%s_roll' % self._project | 287 roll_branch = '%s_roll' % self._project |
275 cwd_kwargs = {'cwd': self._path_to_chrome} | 288 cwd_kwargs = {'cwd': self._path_to_chrome} |
276 subprocess2.check_call(['git', 'clean', '-d', '-f'], **cwd_kwargs) | 289 subprocess2.check_call(['git', 'clean', '-d', '-f'], **cwd_kwargs) |
277 subprocess2.call(['git', 'rebase', '--abort'], **cwd_kwargs) | 290 subprocess2.call(['git', 'rebase', '--abort'], **cwd_kwargs) |
278 subprocess2.call(['git', 'branch', '-D', roll_branch], **cwd_kwargs) | 291 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 | 399 issue = None |
387 | 400 |
388 if issue: | 401 if issue: |
389 if self._rollbot_should_stop(issue): | 402 if self._rollbot_should_stop(issue): |
390 return 1 | 403 return 1 |
391 if not self._maybe_close_active_roll(issue): | 404 if not self._maybe_close_active_roll(issue): |
392 print '%s is still active, nothing to do.' % \ | 405 print '%s is still active, nothing to do.' % \ |
393 self._url_for_issue(issue_number) | 406 self._url_for_issue(issue_number) |
394 return 0 | 407 return 0 |
395 | 408 |
| 409 _do_git_fetch(self._chromium_git_dir) |
| 410 _do_git_fetch(self._project_git_dir) |
| 411 |
396 last_roll_revision = self._last_roll_revision() | 412 last_roll_revision = self._last_roll_revision() |
397 new_roll_revision = self._current_revision() | 413 new_roll_revision = self._current_revision() |
| 414 |
| 415 if not new_roll_revision: |
| 416 raise AutoRollException( |
| 417 'Could not determine the current revision.') |
| 418 |
| 419 if self._git_mode: |
| 420 last_roll_revision_svn = self._last_roll_revision_svn() |
| 421 new_roll_revision_svn = self._current_revision_svn() |
| 422 else: |
| 423 last_roll_revision_svn = None |
| 424 new_roll_revision_svn = None |
| 425 |
398 self._compare_revisions(last_roll_revision, new_roll_revision) | 426 self._compare_revisions(last_roll_revision, new_roll_revision) |
399 | 427 |
400 display_from_rev = ( | 428 display_from_rev = ( |
401 self._short_rev(last_roll_revision) if self._git_mode | 429 self._short_rev(last_roll_revision) if self._git_mode |
402 else last_roll_revision) | 430 else last_roll_revision) |
403 display_to_rev = ( | 431 display_to_rev = ( |
404 self._short_rev(new_roll_revision) if self._git_mode | 432 self._short_rev(new_roll_revision) if self._git_mode |
405 else new_roll_revision) | 433 else new_roll_revision) |
406 commit_msg = self.ROLL_DESCRIPTION_STR % { | 434 commit_msg = self.ROLL_DESCRIPTION_STR % { |
407 'project': self._project.title(), | 435 'project': self._project.title(), |
408 'from_revision': display_from_rev, | 436 'from_revision': display_from_rev, |
409 'to_revision': display_to_rev, | 437 'to_revision': display_to_rev, |
410 } | 438 } |
| 439 |
| 440 if last_roll_revision_svn and new_roll_revision_svn: |
| 441 commit_msg += ' (svn %s:%s)' % ( |
| 442 last_roll_revision_svn, new_roll_revision_svn) |
| 443 |
411 revlink = self._get_revision_link(last_roll_revision, new_roll_revision) | 444 revlink = self._get_revision_link(last_roll_revision, new_roll_revision) |
412 if revlink: | 445 if revlink: |
413 commit_msg += '\n\n' + revlink | 446 commit_msg += '\n\n' + revlink |
414 | 447 |
415 if self._cq_extra_trybots: | 448 if self._cq_extra_trybots: |
416 commit_msg += '\n\n' + CQ_EXTRA_TRYBOTS + ','.join(self._cq_extra_trybots) | 449 commit_msg += '\n\n' + CQ_EXTRA_TRYBOTS + ','.join(self._cq_extra_trybots) |
417 | 450 |
418 self._start_roll(new_roll_revision, commit_msg) | 451 self._start_roll(new_roll_revision, commit_msg) |
419 return 0 | 452 return 0 |
420 | 453 |
(...skipping 15 matching lines...) Expand all Loading... |
436 _, args = parser.parse_args() | 469 _, args = parser.parse_args() |
437 if len(args) != 3: | 470 if len(args) != 3: |
438 parser.print_usage() | 471 parser.print_usage() |
439 return 1 | 472 return 1 |
440 | 473 |
441 AutoRoller(*args).main() | 474 AutoRoller(*args).main() |
442 | 475 |
443 | 476 |
444 if __name__ == '__main__': | 477 if __name__ == '__main__': |
445 sys.exit(main()) | 478 sys.exit(main()) |
OLD | NEW |