Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # coding=utf8 | 1 # coding=utf8 |
| 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 """Commit queue manager class. | 5 """Commit queue manager class. |
| 6 | 6 |
| 7 Security implications: | 7 Security implications: |
| 8 | 8 |
| 9 The following hypothesis are made: | 9 The following hypothesis are made: |
| 10 - Commit queue: | 10 - Commit queue: |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 | 190 |
| 191 raise | 191 raise |
| 192 | 192 |
| 193 # If there is an issue in processed_issues that is not in new_issues, | 193 # If there is an issue in processed_issues that is not in new_issues, |
| 194 # discard it. | 194 # discard it. |
| 195 for pending in self.queue.iterate(): | 195 for pending in self.queue.iterate(): |
| 196 # Note that pending.issue is a int but self.queue.pending_commits keys | 196 # Note that pending.issue is a int but self.queue.pending_commits keys |
| 197 # are str due to json support. | 197 # are str due to json support. |
| 198 if pending.issue not in new_issues: | 198 if pending.issue not in new_issues: |
| 199 logging.info('Flushing issue %d' % pending.issue) | 199 logging.info('Flushing issue %d' % pending.issue) |
| 200 self.context.status.send( | |
| 201 pending, | |
| 202 { 'verification': 'abort', | |
| 203 'payload': { | |
| 204 'output': 'CQ bit was unchecked on CL. Ignoring.' }}) | |
| 205 pending.get_state = lambda: base.IGNORED | 200 pending.get_state = lambda: base.IGNORED |
| 206 self._discard_pending(pending, None) | 201 self._discard_pending(pending, 'CQ bit was unchecked on CL. Ignoring.') |
| 207 | 202 |
| 208 # Find new issues. | 203 # Find new issues. |
| 209 for issue_id in new_issues: | 204 for issue_id in new_issues: |
| 210 if str(issue_id) not in self.queue.pending_commits: | 205 if str(issue_id) not in self.queue.pending_commits: |
| 211 try: | 206 try: |
| 212 issue_data = self.context.rietveld.get_issue_properties( | 207 issue_data = self.context.rietveld.get_issue_properties( |
| 213 issue_id, True) | 208 issue_id, True) |
| 214 except urllib2.HTTPError as e: | 209 except urllib2.HTTPError as e: |
| 215 if e.code in (500, 502, 503): | 210 if e.code in (500, 502, 503): |
| 216 # Temporary AppEngine hiccup. Just log it and continue. | 211 # Temporary AppEngine hiccup. Just log it and continue. |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 264 # Take in account the case where a verifier was removed. | 259 # Take in account the case where a verifier was removed. |
| 265 done = set(pending.verifications.keys()) | 260 done = set(pending.verifications.keys()) |
| 266 missing = expected - done | 261 missing = expected - done |
| 267 if (not missing or pending.get_state() != base.PROCESSING): | 262 if (not missing or pending.get_state() != base.PROCESSING): |
| 268 continue | 263 continue |
| 269 logging.info( | 264 logging.info( |
| 270 'Processing issue %s (%s, %d)' % ( | 265 'Processing issue %s (%s, %d)' % ( |
| 271 pending.issue, missing, pending.get_state())) | 266 pending.issue, missing, pending.get_state())) |
| 272 self._verify_pending(pending) | 267 self._verify_pending(pending) |
| 273 except base.DiscardPending as e: | 268 except base.DiscardPending as e: |
| 274 self._discard_pending(e.pending, e.status) | 269 self._discard_pending(e.pending, e.status or |
|
Paweł Hajdan Jr.
2014/01/28 01:13:46
nit: Indentation in these "or" expressions might b
| |
| 270 'process_new_pending_commit: ' + self.FAILED_NO_MESSAGE) | |
| 275 | 271 |
| 276 def update_status(self): | 272 def update_status(self): |
| 277 """Updates the status for each pending commit verifier.""" | 273 """Updates the status for each pending commit verifier.""" |
| 278 why_nots = dict((p.issue, p.why_not()) for p in self.queue.iterate()) | 274 why_nots = dict((p.issue, p.why_not()) for p in self.queue.iterate()) |
| 279 | 275 |
| 280 for verifier in self.all_verifiers: | 276 for verifier in self.all_verifiers: |
| 281 try: | 277 try: |
| 282 verifier.update_status(self.queue.iterate()) | 278 verifier.update_status(self.queue.iterate()) |
| 283 except base.DiscardPending as e: | 279 except base.DiscardPending as e: |
| 284 # It's not efficient since it takes a full loop for each pending | 280 # It's not efficient since it takes a full loop for each pending |
| 285 # commit to discard. | 281 # commit to discard. |
| 286 self._discard_pending(e.pending, e.status) | 282 self._discard_pending(e.pending, e.status or |
| 283 'update_status: ' + self.FAILED_NO_MESSAGE) | |
| 287 | 284 |
| 288 for pending in self.queue.iterate(): | 285 for pending in self.queue.iterate(): |
| 289 why_not = pending.why_not() | 286 why_not = pending.why_not() |
| 290 if why_nots[pending.issue] != why_not: | 287 if why_nots[pending.issue] != why_not: |
| 291 self.context.status.send( | 288 self.context.status.send( |
| 292 pending, | 289 pending, |
| 293 {'verification': 'why not', | 290 {'verification': 'why not', |
| 294 'payload': {'message': why_not}}) | 291 'payload': {'message': why_not}}) |
| 295 | 292 |
| 296 | 293 |
| 297 def scan_results(self): | 294 def scan_results(self): |
| 298 """Scans pending commits that can be committed or discarded.""" | 295 """Scans pending commits that can be committed or discarded.""" |
| 299 for pending in self.queue.iterate(): | 296 for pending in self.queue.iterate(): |
| 300 state = pending.get_state() | 297 state = pending.get_state() |
| 301 if state == base.FAILED: | 298 if state == base.FAILED: |
| 302 self._discard_pending( | 299 self._discard_pending( |
| 303 pending, pending.error_message() or self.FAILED_NO_MESSAGE) | 300 pending, pending.error_message() or |
| 301 'scan_results(FAILED): ' + self.FAILED_NO_MESSAGE) | |
| 304 elif state == base.SUCCEEDED: | 302 elif state == base.SUCCEEDED: |
| 305 if self._throttle(pending): | 303 if self._throttle(pending): |
| 306 continue | 304 continue |
| 307 try: | 305 try: |
| 308 # Runs checks. It's be nice to run the test before the postpone, | 306 # Runs checks. It's be nice to run the test before the postpone, |
| 309 # especially if the tree is closed for a long moment but at the same | 307 # especially if the tree is closed for a long moment but at the same |
| 310 # time it would keep fetching the rietveld status constantly. | 308 # time it would keep fetching the rietveld status constantly. |
| 311 self._last_minute_checks(pending) | 309 self._last_minute_checks(pending) |
| 312 self.context.status.send( | 310 self.context.status.send( |
| 313 pending, | 311 pending, |
| 314 {'verification': 'why not', | 312 {'verification': 'why not', |
| 315 'payload': {'message': ''}}) | 313 'payload': {'message': ''}}) |
| 316 | 314 |
| 317 self._commit_patch(pending) | 315 self._commit_patch(pending) |
| 318 except base.DiscardPending as e: | 316 except base.DiscardPending as e: |
| 319 self._discard_pending(e.pending, e.status) | 317 self._discard_pending(e.pending, e.status or |
| 318 'scan_results(commit): ' + self.FAILED_NO_MESSAGE) | |
| 320 except Exception as e: | 319 except Exception as e: |
| 321 self._discard_pending(pending, self.INTERNAL_EXCEPTION) | 320 self._discard_pending(pending, self.INTERNAL_EXCEPTION) |
| 322 raise | 321 raise |
| 323 else: | 322 else: |
| 324 # When state is IGNORED, we need to keep this issue so it's not fetched | 323 # When state is IGNORED, we need to keep this issue so it's not fetched |
| 325 # another time but we can't discard it since we don't want to remove the | 324 # another time but we can't discard it since we don't want to remove the |
| 326 # commit bit for another project hosted on the same code review | 325 # commit bit for another project hosted on the same code review |
| 327 # instance. | 326 # instance. |
| 328 assert state in (base.PROCESSING, base.IGNORED) | 327 assert state in (base.PROCESSING, base.IGNORED) |
| 329 | 328 |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 416 pending, | 415 pending, |
| 417 'List of reviewers changed. %s did a drive-by without LGTM\'ing!' % | 416 'List of reviewers changed. %s did a drive-by without LGTM\'ing!' % |
| 418 ','.join(drivers_by)) | 417 ','.join(drivers_by)) |
| 419 if pending.patchset != pending_data['patchsets'][-1]: | 418 if pending.patchset != pending_data['patchsets'][-1]: |
| 420 raise base.DiscardPending(pending, | 419 raise base.DiscardPending(pending, |
| 421 'Commit queue failed due to new patchset.') | 420 'Commit queue failed due to new patchset.') |
| 422 | 421 |
| 423 def _discard_pending(self, pending, message): | 422 def _discard_pending(self, pending, message): |
| 424 """Discards a pending commit. Attach an optional message to the review.""" | 423 """Discards a pending commit. Attach an optional message to the review.""" |
| 425 logging.debug('_discard_pending(%s, %s)', pending.issue, message) | 424 logging.debug('_discard_pending(%s, %s)', pending.issue, message) |
| 425 msg = message or self.FAILED_NO_MESSAGE | |
| 426 try: | 426 try: |
| 427 try: | 427 try: |
| 428 if pending.get_state() != base.IGNORED: | 428 if pending.get_state() != base.IGNORED: |
| 429 self.context.rietveld.set_flag( | 429 self.context.rietveld.set_flag( |
| 430 pending.issue, pending.patchset, 'commit', False) | 430 pending.issue, pending.patchset, 'commit', False) |
| 431 except urllib2.HTTPError as e: | 431 except urllib2.HTTPError as e: |
| 432 logging.error( | 432 logging.error( |
| 433 'Failed to set the flag to False for %s with message %s' % ( | 433 'Failed to set the flag to False for %s with message %s' % ( |
| 434 pending.pending_name(), message)) | 434 pending.pending_name(), msg)) |
| 435 traceback.print_stack() | 435 traceback.print_stack() |
| 436 logging.error(str(e)) | 436 logging.error(str(e)) |
| 437 errors.send_stack(e) | 437 errors.send_stack(e) |
| 438 if message: | 438 try: |
| 439 try: | 439 self.context.rietveld.add_comment(pending.issue, msg) |
| 440 self.context.rietveld.add_comment(pending.issue, message) | 440 except urllib2.HTTPError as e: |
| 441 except urllib2.HTTPError as e: | 441 logging.error( |
| 442 logging.error( | 442 'Failed to add comment for %s with message %s' % ( |
| 443 'Failed to add comment for %s with message %s' % ( | 443 pending.pending_name(), msg)) |
| 444 pending.pending_name(), message)) | 444 traceback.print_stack() |
| 445 traceback.print_stack() | 445 errors.send_stack(e) |
| 446 errors.send_stack(e) | 446 self.context.status.send( |
| 447 self.context.status.send( | 447 pending, |
| 448 pending, | 448 { 'verification': 'abort', |
| 449 { 'verification': 'abort', | 449 'payload': { |
| 450 'payload': { | 450 'output': msg }}) |
| 451 'output': message }}) | |
| 452 finally: | 451 finally: |
| 453 # Most importantly, remove the PendingCommit from the queue. | 452 # Most importantly, remove the PendingCommit from the queue. |
| 454 self.queue.remove(pending.issue) | 453 self.queue.remove(pending.issue) |
| 455 | 454 |
| 456 def _commit_patch(self, pending): | 455 def _commit_patch(self, pending): |
| 457 """Commits the pending patch to the repository. | 456 """Commits the pending patch to the repository. |
| 458 | 457 |
| 459 Do the checkout and applies the patch. | 458 Do the checkout and applies the patch. |
| 460 """ | 459 """ |
| 461 try: | 460 try: |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 519 self.queue.remove(pending.issue) | 518 self.queue.remove(pending.issue) |
| 520 except ( | 519 except ( |
| 521 checkout.PatchApplicationFailed, patch.UnsupportedPatchFormat) as e: | 520 checkout.PatchApplicationFailed, patch.UnsupportedPatchFormat) as e: |
| 522 raise base.DiscardPending(pending, str(e)) | 521 raise base.DiscardPending(pending, str(e)) |
| 523 except subprocess2.CalledProcessError as e: | 522 except subprocess2.CalledProcessError as e: |
| 524 stdout = getattr(e, 'stdout', None) | 523 stdout = getattr(e, 'stdout', None) |
| 525 out = 'Failed to apply the patch.' | 524 out = 'Failed to apply the patch.' |
| 526 if stdout: | 525 if stdout: |
| 527 out += '\n%s' % stdout | 526 out += '\n%s' % stdout |
| 528 raise base.DiscardPending(pending, out) | 527 raise base.DiscardPending(pending, out) |
| 528 # Apply default message here | |
|
Paweł Hajdan Jr.
2014/01/28 01:13:46
nit: This comment line seems to be out of place -
| |
| 529 except base.DiscardPending as e: | 529 except base.DiscardPending as e: |
| 530 self._discard_pending(e.pending, e.status) | 530 self._discard_pending(e.pending, e.status or |
| 531 '_commit_patch: ' + self.FAILED_NO_MESSAGE) | |
| 531 | 532 |
| 532 def _throttle(self, pending): | 533 def _throttle(self, pending): |
| 533 """Returns True if a commit should be delayed.""" | 534 """Returns True if a commit should be delayed.""" |
| 534 if pending.postpone(): | 535 if pending.postpone(): |
| 535 self.context.status.send( | 536 self.context.status.send( |
| 536 pending, | 537 pending, |
| 537 {'verification': 'why not', | 538 {'verification': 'why not', |
| 538 'payload': { | 539 'payload': { |
| 539 'message': pending.why_not()}}) | 540 'message': pending.why_not()}}) |
| 540 return True | 541 return True |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 560 """Loads the commit queue state from a JSON file.""" | 561 """Loads the commit queue state from a JSON file.""" |
| 561 self.queue = model.load_from_json_file(filename) | 562 self.queue = model.load_from_json_file(filename) |
| 562 | 563 |
| 563 def save(self, filename): | 564 def save(self, filename): |
| 564 """Save the commit queue state in a simple JSON file.""" | 565 """Save the commit queue state in a simple JSON file.""" |
| 565 model.save_to_json_file(filename, self.queue) | 566 model.save_to_json_file(filename, self.queue) |
| 566 | 567 |
| 567 def close(self): | 568 def close(self): |
| 568 """Close all the active pending manager items.""" | 569 """Close all the active pending manager items.""" |
| 569 self.context.status.close() | 570 self.context.status.close() |
| OLD | NEW |