@@ -70,7 +70,9 @@ Use `r?` to explicitly pick a reviewer";
7070const RETURNING_USER_WELCOME_MESSAGE_NO_REVIEWER : & str =
7171 "@{author}: no appropriate reviewer found, use `r?` to override" ;
7272
73- const ON_VACATION_WARNING : & str = "{username} is on vacation. Please do not assign them to PRs." ;
73+ const ON_VACATION_WARNING : & str = "{username} is on vacation.
74+
75+ Please choose another assignee." ;
7476
7577const NON_DEFAULT_BRANCH : & str =
7678 "Pull requests are usually filed against the {default} branch for this repo, \
@@ -105,9 +107,14 @@ Please contact us on [#t-infra](https://rust-lang.zulipchat.com/#narrow/stream/2
105107
106108cc: @jackh726 @apiraino" ;
107109
108- fn on_vacation_msg ( user : & str ) -> String {
109- ON_VACATION_WARNING . replace ( "{username}" , user)
110- }
110+ const REVIEWER_IS_PR_AUTHOR : & str = "Pull request author cannot be assigned as reviewer.
111+
112+ Please choose another assignee." ;
113+
114+ const REVIEWER_ALREADY_ASSIGNED : & str =
115+ "Requested reviewer is already assigned to this pull request.
116+
117+ Please choose another assignee." ;
111118
112119#[ derive( Debug , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
113120struct AssignData {
@@ -354,16 +361,24 @@ async fn determine_assignee(
354361 is there maybe a misconfigured group?",
355362 event. issue. global_id( )
356363 ) ,
357- // TODO: post a comment on the PR if the reviewers were filtered due to being on vacation
358364 Err (
359365 e @ FindReviewerError :: NoReviewer { .. }
360366 | e @ FindReviewerError :: AllReviewersFiltered { .. }
361367 | e @ FindReviewerError :: NoReviewerHasCapacity
362- | e @ FindReviewerError :: ReviewerHasNoCapacity { .. } ,
368+ | e @ FindReviewerError :: ReviewerHasNoCapacity { .. }
369+ | e @ FindReviewerError :: ReviewerIsPrAuthor { .. }
370+ | e @ FindReviewerError :: ReviewerAlreadyAssigned { .. } ,
363371 ) => log:: trace!(
364372 "no reviewer could be determined for PR {}: {e}" ,
365373 event. issue. global_id( )
366374 ) ,
375+ Err ( e @ FindReviewerError :: ReviewerOnVacation { .. } ) => {
376+ // TODO: post a comment on the PR if the reviewer(s) were filtered due to being on vacation
377+ log:: trace!(
378+ "no reviewer could be determined for PR {}: {e}" ,
379+ event. issue. global_id( )
380+ )
381+ }
367382 }
368383 }
369384 // If no owners matched the diff, fall-through.
@@ -508,7 +523,10 @@ pub(super) async fn handle_command(
508523 {
509524 // This is a comment, so there must already be a reviewer assigned. No need to assign anyone else.
510525 issue
511- . post_comment ( & ctx. github , & on_vacation_msg ( & username) )
526+ . post_comment (
527+ & ctx. github ,
528+ & ON_VACATION_WARNING . replace ( "{username}" , & username) ,
529+ )
512530 . await ?;
513531 return Ok ( ( ) ) ;
514532 }
@@ -544,8 +562,9 @@ pub(super) async fn handle_command(
544562 let work_queue = has_user_capacity ( & db_client, & name) . await ;
545563 if work_queue. is_err ( ) {
546564 // NOTE: disabled for now, just log
547- log:: info!(
548- "DB reported that user {} has no review capacity. Ignoring." ,
565+ log:: warn!(
566+ "[#{}] PR self-assign failed, DB reported that user {} has no review capacity. Ignoring." ,
567+ issue. number,
549568 name
550569 ) ;
551570 // issue
@@ -702,6 +721,13 @@ pub enum FindReviewerError {
702721 /// The requested reviewer has no capacity to accept a pull request
703722 /// assignment at this time
704723 ReviewerHasNoCapacity { username : String } ,
724+ /// Requested reviewer is on vacation
725+ /// (i.e. username is in [users_on_vacation] in the triagebot.toml)
726+ ReviewerOnVacation { username : String } ,
727+ /// Requested reviewer is PR author
728+ ReviewerIsPrAuthor { username : String } ,
729+ /// Requested reviewer is already assigned to that PR
730+ ReviewerAlreadyAssigned { username : String } ,
705731}
706732
707733impl std:: error:: Error for FindReviewerError { }
@@ -747,6 +773,23 @@ impl fmt::Display for FindReviewerError {
747773 FindReviewerError :: NoReviewerHasCapacity => {
748774 write ! ( f, "{}" , NO_REVIEWER_HAS_CAPACITY )
749775 }
776+ FindReviewerError :: ReviewerOnVacation { username } => {
777+ write ! ( f, "{}" , ON_VACATION_WARNING . replace( "{username}" , username) )
778+ }
779+ FindReviewerError :: ReviewerIsPrAuthor { username } => {
780+ write ! (
781+ f,
782+ "{}" ,
783+ REVIEWER_IS_PR_AUTHOR . replace( "{username}" , username)
784+ )
785+ }
786+ FindReviewerError :: ReviewerAlreadyAssigned { username } => {
787+ write ! (
788+ f,
789+ "{}" ,
790+ REVIEWER_ALREADY_ASSIGNED . replace( "{username}" , username)
791+ )
792+ }
750793 }
751794 }
752795}
@@ -785,7 +828,11 @@ async fn find_reviewer_from_names(
785828 // These are all ideas for improving the selection here. However, I'm not
786829 // sure they are really worth the effort.
787830
788- log:: info!( "Initial unfiltered list of candidates: {:?}" , candidates) ;
831+ log:: info!(
832+ "[#{}] Initial unfiltered list of candidates: {:?}" ,
833+ issue. number,
834+ candidates
835+ ) ;
789836
790837 // Special case user "ghost", we always skip filtering
791838 if candidates. contains ( "ghost" ) {
@@ -799,14 +846,18 @@ async fn find_reviewer_from_names(
799846
800847 if filtered_candidates. is_empty ( ) {
801848 // NOTE: disabled for now, just log
802- log:: info!( "Filtered list of PR assignee is empty" ) ;
849+ log:: info!( "[#{}] Filtered list of PR assignee is empty" , issue . number ) ;
803850 // return Err(FindReviewerError::AllReviewersFiltered {
804851 // initial: names.to_vec(),
805852 // filtered: names.to_vec(),
806853 // });
807854 }
808855
809- log:: info!( "Filtered list of candidates: {:?}" , filtered_candidates) ;
856+ log:: info!(
857+ "[#{}] Filtered list of candidates: {:?}" ,
858+ issue. number,
859+ filtered_candidates
860+ ) ;
810861
811862 // Return unfiltered list of candidates
812863 Ok ( candidates
@@ -862,21 +913,45 @@ fn candidate_reviewers_from_names<'a>(
862913 let mut group_expansion: Vec < & str > = names. iter ( ) . map ( |n| n. as_str ( ) ) . collect ( ) ;
863914 // Keep track of which users get filtered out for a better error message.
864915 let mut filtered = Vec :: new ( ) ;
916+ // For debugging purposes, keep track about /why/ candidates were filtered out
917+ let mut filtered_debug: HashMap < String , Option < FindReviewerError > > = HashMap :: new ( ) ;
865918 let repo = issue. repository ( ) ;
866919 let org_prefix = format ! ( "{}/" , repo. organization) ;
867920 // Don't allow groups or teams to include the current author or assignee.
868921 let mut filter = |name : & & str | -> bool {
869922 let name_lower = name. to_lowercase ( ) ;
870- let ok = name_lower != issue. user . login . to_lowercase ( )
871- && !config. is_on_vacation ( name)
872- && !issue
873- . assignees
874- . iter ( )
875- . any ( |assignee| name_lower == assignee. login . to_lowercase ( ) ) ;
876- if !ok {
923+ let is_pr_author = name_lower == issue. user . login . to_lowercase ( ) ;
924+ let is_on_vacation = config. is_on_vacation ( name) ;
925+ let is_already_assigned = issue
926+ . assignees
927+ . iter ( )
928+ . any ( |assignee| name_lower == assignee. login . to_lowercase ( ) ) ;
929+
930+ // Record the reason why the candidate was filtered out
931+ let reason = {
932+ if is_pr_author {
933+ Some ( FindReviewerError :: ReviewerIsPrAuthor {
934+ username : name. to_string ( ) ,
935+ } )
936+ } else if is_on_vacation {
937+ Some ( FindReviewerError :: ReviewerOnVacation {
938+ username : name. to_string ( ) ,
939+ } )
940+ } else if is_already_assigned {
941+ Some ( FindReviewerError :: ReviewerAlreadyAssigned {
942+ username : name. to_string ( ) ,
943+ } )
944+ } else {
945+ None
946+ }
947+ } ;
948+
949+ let can_be_assigned = !is_pr_author && !is_on_vacation && !is_already_assigned;
950+ if !can_be_assigned {
877951 filtered. push ( name. to_string ( ) ) ;
952+ filtered_debug. insert ( name. to_string ( ) , reason) ;
878953 }
879- ok
954+ can_be_assigned
880955 } ;
881956
882957 // Loop over groups to recursively expand them.
@@ -934,6 +1009,12 @@ fn candidate_reviewers_from_names<'a>(
9341009 if filtered. is_empty ( ) {
9351010 Err ( FindReviewerError :: NoReviewer { initial } )
9361011 } else {
1012+ log:: warn!(
1013+ "[#{}] Initial list of candidates {:?}, filtered-out with reasons: {:?}" ,
1014+ issue. number,
1015+ initial,
1016+ filtered_debug
1017+ ) ;
9371018 Err ( FindReviewerError :: AllReviewersFiltered { initial, filtered } )
9381019 }
9391020 } else {
0 commit comments