Skip to content

Commit 82073e7

Browse files
authored
Merge pull request #1712 from jyn514/vacation
Add a new `users_on_vacation` config which prevents PR assignment
2 parents 37d04d7 + c26e5a1 commit 82073e7

File tree

3 files changed

+65
-2
lines changed

3 files changed

+65
-2
lines changed

src/config.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ pub(crate) struct AssignConfig {
9090
/// usernames, team names, or ad-hoc groups.
9191
#[serde(default)]
9292
pub(crate) owners: HashMap<String, Vec<String>>,
93+
#[serde(default)]
94+
pub(crate) users_on_vacation: HashSet<String>,
95+
}
96+
97+
impl AssignConfig {
98+
pub(crate) fn is_on_vacation(&self, user: &str) -> bool {
99+
let name_lower = user.to_lowercase();
100+
self.users_on_vacation
101+
.iter()
102+
.any(|vacationer| name_lower == vacationer.to_lowercase())
103+
}
93104
}
94105

95106
#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
@@ -337,6 +348,7 @@ mod tests {
337348
]
338349
339350
[assign]
351+
users_on_vacation = ["jyn514"]
340352
341353
[note]
342354
@@ -393,6 +405,7 @@ mod tests {
393405
contributing_url: None,
394406
adhoc_groups: HashMap::new(),
395407
owners: HashMap::new(),
408+
users_on_vacation: HashSet::from(["jyn514".into()]),
396409
}),
397410
note: Some(NoteConfig { _empty: () }),
398411
ping: Some(PingConfig { teams: ping_teams }),

src/handlers/assign.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,19 @@ const RETURNING_USER_WELCOME_MESSAGE: &str = "r? @{assignee}
6161
const RETURNING_USER_WELCOME_MESSAGE_NO_REVIEWER: &str =
6262
"@{author}: no appropriate reviewer found, use r? to override";
6363

64+
const ON_VACATION_WARNING: &str = "{username} is on vacation. Please do not assign them to PRs.";
65+
6466
const NON_DEFAULT_BRANCH: &str =
6567
"Pull requests are usually filed against the {default} branch for this repo, \
6668
but this one is against {target}. \
6769
Please double check that you specified the right target!";
6870

6971
const SUBMODULE_WARNING_MSG: &str = "These commits modify **submodules**.";
7072

73+
fn on_vacation_msg(user: &str) -> String {
74+
ON_VACATION_WARNING.replace("{username}", user)
75+
}
76+
7177
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
7278
struct AssignData {
7379
user: Option<String>,
@@ -295,6 +301,7 @@ async fn determine_assignee(
295301
is there maybe a misconfigured group?",
296302
event.issue.global_id()
297303
),
304+
// TODO: post a comment on the PR if the reviewers were filtered due to being on vacation
298305
Err(
299306
e @ FindReviewerError::NoReviewer { .. }
300307
| e @ FindReviewerError::AllReviewersFiltered { .. },
@@ -438,7 +445,19 @@ pub(super) async fn handle_command(
438445
}
439446
let username = match cmd {
440447
AssignCommand::Own => event.user().login.clone(),
441-
AssignCommand::User { username } => username,
448+
AssignCommand::User { username } => {
449+
// Allow users on vacation to assign themselves to a PR, but not anyone else.
450+
if config.is_on_vacation(&username)
451+
&& event.user().login.to_lowercase() != username.to_lowercase()
452+
{
453+
// This is a comment, so there must already be a reviewer assigned. No need to assign anyone else.
454+
issue
455+
.post_comment(&ctx.github, &on_vacation_msg(&username))
456+
.await?;
457+
return Ok(());
458+
}
459+
username
460+
}
442461
AssignCommand::Release => {
443462
log::trace!(
444463
"ignoring release on PR {:?}, must always have assignee",
@@ -604,7 +623,7 @@ impl fmt::Display for FindReviewerError {
604623
write!(
605624
f,
606625
"Could not assign reviewer from: `{}`.\n\
607-
User(s) `{}` are either the PR author or are already assigned, \
626+
User(s) `{}` are either the PR author, already assigned, or on vacation, \
608627
and there are no other candidates.\n\
609628
Use r? to specify someone else to assign.",
610629
initial.join(","),
@@ -680,6 +699,7 @@ fn candidate_reviewers_from_names<'a>(
680699
let mut filter = |name: &&str| -> bool {
681700
let name_lower = name.to_lowercase();
682701
let ok = name_lower != issue.user.login.to_lowercase()
702+
&& !config.is_on_vacation(name)
683703
&& !issue
684704
.assignees
685705
.iter()

src/handlers/assign/tests/tests_candidates.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,33 @@ fn invalid_org_doesnt_match() {
265265
)),
266266
);
267267
}
268+
269+
#[test]
270+
fn vacation() {
271+
let teams = toml::toml!(bootstrap = ["jyn514", "Mark-Simulacrum"]);
272+
let config = toml::toml!(users_on_vacation = ["jyn514"]);
273+
let issue = generic_issue("octocat", "rust-lang/rust");
274+
275+
// Test that `r? user` falls through to assigning from the team.
276+
// See `determine_assignee` - ideally we would test that function directly instead of indirectly through `find_reviewer_from_names`.
277+
let err_names = vec!["jyn514".into()];
278+
test_from_names(
279+
Some(teams.clone()),
280+
config.clone(),
281+
issue.clone(),
282+
&["jyn514"],
283+
Err(FindReviewerError::AllReviewersFiltered {
284+
initial: err_names.clone(),
285+
filtered: err_names,
286+
}),
287+
);
288+
289+
// Test that `r? bootstrap` doesn't assign from users on vacation.
290+
test_from_names(
291+
Some(teams.clone()),
292+
config.clone(),
293+
issue,
294+
&["bootstrap"],
295+
Ok(&["Mark-Simulacrum"]),
296+
);
297+
}

0 commit comments

Comments
 (0)