@@ -235,9 +235,9 @@ def get_review_decision(self):
235
235
236
236
def get_reviews (self , complete = False ):
237
237
r"""
238
- Return the list of reviews of the PR. Per default only those reviews
239
- are returned which have been submitted after the youngest commit.
240
- Use keyword ``complete`` to get them all.
238
+ Return the list of reviews of the PR. Per default only those proper reviews
239
+ are returned which have been submitted after the most recent commit. Use
240
+ keyword ``complete`` to get them all.
241
241
"""
242
242
if not self .is_pull_request ():
243
243
return None
@@ -253,9 +253,11 @@ def get_reviews(self, complete=False):
253
253
self .get_commits ()
254
254
255
255
date = self ._commit_date
256
+ no_rev = ReviewDecision .unclear .value
256
257
new_revs = [rev for rev in self ._reviews if rev ['submittedAt' ] > date ]
257
- info ('Reviews for %s: %s after %s' % (self ._issue , self ._reviews , date ))
258
- return new_revs
258
+ proper_new_revs = [rev for rev in new_revs if rev ['state' ] != no_rev ]
259
+ info ('Proper reviews after %s for %s: %s' % (date , self ._issue , proper_new_revs ))
260
+ return proper_new_revs
259
261
260
262
def active_partners (self , item ):
261
263
r"""
@@ -270,12 +272,34 @@ def active_partners(self, item):
270
272
# -------------------------------------------------------------------------
271
273
# methods to validate the issue state
272
274
# -------------------------------------------------------------------------
275
+ def review_comment_to_state (self ):
276
+ r"""
277
+ Return a State label if the most recent review comment
278
+ starts with its value.
279
+ """
280
+ revs = self .get_reviews (complete = True )
281
+ date = max (rev ['submittedAt' ] for rev in revs )
282
+
283
+ for rev in revs :
284
+ if rev ['submittedAt' ] == date :
285
+ for stat in State :
286
+ body = rev ['body' ]
287
+ if body .startswith (stat .value ):
288
+ return stat
289
+ return None
290
+
273
291
def needs_work_valid (self ):
274
292
r"""
275
293
Return ``True`` if the PR needs work. This is the case if
276
- the review decision requests changes or if there is any
277
- review reqesting changes.
294
+ there are reviews more recent than any commit and the review
295
+ decision requests changes or if there is any review reqesting
296
+ changes.
278
297
"""
298
+ revs = self .get_reviews ()
299
+ if not revs :
300
+ # no proper review since most recent commit.
301
+ return False
302
+
279
303
ch_req = ReviewDecision .changes_requested
280
304
rev_dec = self .get_review_decision ()
281
305
if rev_dec :
@@ -286,8 +310,6 @@ def needs_work_valid(self):
286
310
info ('PR %s doesn\' t need work (by decision)' % self ._issue )
287
311
return False
288
312
289
- revs = self .get_reviews ()
290
- revs = [rev for rev in revs if rev ['author' ]['login' ] == self ._actor ]
291
313
if any (rev ['state' ] == ch_req .value for rev in revs ):
292
314
info ('PR %s needs work' % self ._issue )
293
315
return True
@@ -297,9 +319,15 @@ def needs_work_valid(self):
297
319
def positive_review_valid (self ):
298
320
r"""
299
321
Return ``True`` if the PR has positive review. This is the
300
- case if the review decision is approved or if there is any
301
- approved review but no changes requesting one.
322
+ case if there are reviews more recent than any commit and the
323
+ review decision is approved or if there is any approved review
324
+ but no changes requesting one.
302
325
"""
326
+ revs = self .get_reviews ()
327
+ if not revs :
328
+ # no proper review since most recent commit.
329
+ return False
330
+
303
331
appr = ReviewDecision .approved
304
332
rev_dec = self .get_review_decision ()
305
333
if rev_dec :
@@ -310,13 +338,7 @@ def positive_review_valid(self):
310
338
info ('PR %s doesn\' t have positve review (by decision)' % self ._issue )
311
339
return False
312
340
313
- if self .needs_work_valid ():
314
- info ('PR %s doesn\' t have positve review (needs work)' % self ._issue )
315
- return False
316
-
317
- revs = self .get_reviews ()
318
- revs = [rev for rev in revs if rev ['author' ]['login' ] == self ._actor ]
319
- if any (rev ['state' ] == appr .value for rev in revs ):
341
+ if all (rev ['state' ] == appr .value for rev in revs ):
320
342
info ('PR %s has positve review' % self ._issue )
321
343
return True
322
344
info ('PR %s doesn\' t have positve review' % self ._issue )
@@ -407,6 +429,12 @@ def edit(self, arg, option):
407
429
"""
408
430
self .gh_cmd ('edit' , arg , option )
409
431
432
+ def mark_as_ready (self ):
433
+ r"""
434
+ Perform a system call to ``gh`` to mark a PR as ready for review.
435
+ """
436
+ self .gh_cmd ('ready' , '' , '' )
437
+
410
438
def review (self , arg , text ):
411
439
r"""
412
440
Perform a system call to ``gh`` to review a PR.
@@ -427,6 +455,13 @@ def request_changes(self):
427
455
self .review ('--request-changes' , '%s requested changes for this PR' % self ._actor )
428
456
info ('Changes requested for PR %s by %s' % (self ._issue , self ._actor ))
429
457
458
+ def review_comment (self , text ):
459
+ r"""
460
+ Add a review comment.
461
+ """
462
+ self .review ('--comment' , text )
463
+ info ('Add review comment for PR %s: %s' % (self ._issue , text ))
464
+
430
465
def add_comment (self , text ):
431
466
r"""
432
467
Perform a system call to ``gh`` to add a comment to an issue or PR.
@@ -526,20 +561,34 @@ def on_label_add(self, label):
526
561
return
527
562
528
563
if item == State .needs_review :
529
- if not self .needs_review_valid ():
564
+ if self .needs_review_valid ():
565
+ # here we come for example after a sequence:
566
+ # needs review -> needs info -> needs review
567
+ pass
568
+ elif self .is_draft ():
569
+ self .mark_as_ready ()
570
+ else :
530
571
self .reject_label_addition (item )
531
572
return
532
573
533
- if item == State .positive_review :
534
- if self .approve_allowed ():
535
- self .approve ()
574
+ if item == State .needs_work :
575
+ if self .needs_work_valid ():
576
+ # here we come for example after a sequence:
577
+ # needs work -> needs info -> needs work
578
+ pass
579
+ elif not self .is_draft ():
580
+ self .request_changes ()
536
581
else :
537
582
self .reject_label_addition (item )
538
583
return
539
584
540
- if item == State .needs_work :
541
- if self .needs_review_valid ():
542
- self .request_changes ()
585
+ if item == State .positive_review :
586
+ if self .positive_review_valid ():
587
+ # here we come for example after a sequence:
588
+ # positive review -> needs info -> positive review
589
+ pass
590
+ elif self .approve_allowed ():
591
+ self .approve ()
543
592
else :
544
593
self .reject_label_addition (item )
545
594
return
@@ -570,6 +619,19 @@ def on_label_removal(self, label):
570
619
elif sel_list is Priority :
571
620
self .reject_label_removal (item )
572
621
return
622
+
623
+ def on_review_comment (self ):
624
+ r"""
625
+ Check if the text of the most recent review begins with a
626
+ specific label name. In this case, simulate the corresponding
627
+ label addition. This feature is needed for people who don't
628
+ have permission to add labels (i.e. aren't a member of the
629
+ Triage team).
630
+ """
631
+ rev_state = self .review_comment_to_state ()
632
+ if rev_state in (State .needs_info , State .needs_review ):
633
+ self .select_label (rev_state )
634
+ self .run (Action .labeled , label = rev_state .value )
573
635
574
636
def remove_all_labels_of_sel_list (self , sel_list ):
575
637
r"""
@@ -605,6 +667,7 @@ def run(self, action, label=None, rev_state=None):
605
667
self .select_label (State .needs_review )
606
668
607
669
if action is Action .submitted :
670
+ rev_state = RevState (rev_state )
608
671
if rev_state is RevState .approved :
609
672
if self .positive_review_valid ():
610
673
self .select_label (State .positive_review )
@@ -613,6 +676,9 @@ def run(self, action, label=None, rev_state=None):
613
676
if self .needs_work_valid ():
614
677
self .select_label (State .needs_work )
615
678
679
+ if rev_state is RevState .commented :
680
+ self .on_review_comment ()
681
+
616
682
def run_tests (self ):
617
683
r"""
618
684
Simulative run over all posibble events.
@@ -644,12 +710,17 @@ def run_tests(self):
644
710
self .add_label (res .value )
645
711
self .run (action , label = prio .value )
646
712
elif action == Action .submitted and self .is_pull_request ():
647
- for stat in RevState :
648
- if stat is RevState .approved :
713
+ for rev_stat in RevState :
714
+ if rev_stat is RevState .approved :
649
715
self .approve ()
650
- elif stat is RevState .changes_requested :
716
+ self .run (action , rev_state = rev_stat .value )
717
+ elif rev_stat is RevState .changes_requested :
651
718
self .request_changes ()
652
- self .run (action , rev_state = stat .value )
719
+ self .run (action , rev_state = rev_stat .value )
720
+ elif rev_stat is RevState .commented :
721
+ for stat in State :
722
+ self .review_comment (stat .value )
723
+ self .run (action , rev_state = rev_stat .value )
653
724
elif self .is_pull_request ():
654
725
self .run (action )
655
726
0 commit comments