Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(257)

Side by Side Diff: pending_manager.py

Issue 138173005: CQ to always post a message when Commit box is unchecked. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/commit-queue
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/pending_manager_test.py » ('j') | verification/fake.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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()
OLDNEW
« no previous file with comments | « no previous file | tests/pending_manager_test.py » ('j') | verification/fake.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698