review classifier: a setting makes the PR review list only the failed rules
failures_only (off by default, also for a stored document without it) makes the review the classifier posts on a PR list just the rows that failed their rule, or say that no check failed on an approval. A review with no clean verdict and a try-out keep every row. The classifier settings form gets a checkbox for it.
This commit is contained in:
parent
818d41eef3
commit
cf5746188a
1 changed files with 138 additions and 9 deletions
|
|
@ -855,7 +855,12 @@ enum Outcome {
|
|||
/// Nothing reached the classifier, for this reason.
|
||||
NotRun(String),
|
||||
/// The classifier answered a run of `sent` classifications.
|
||||
Ran { sent: usize, reply: Reply },
|
||||
Ran {
|
||||
sent: usize,
|
||||
reply: Reply,
|
||||
/// Whether the review lists only the rules that failed.
|
||||
failures_only: bool,
|
||||
},
|
||||
}
|
||||
|
||||
/// Read the rules, run them on `pull`, the PR as it was read, and post the
|
||||
|
|
@ -1106,6 +1111,7 @@ async fn classify(
|
|||
);
|
||||
}
|
||||
};
|
||||
let failures_only = settings.failures_only;
|
||||
let request = TryOut {
|
||||
operator: req.requester.clone(),
|
||||
repo,
|
||||
|
|
@ -1123,6 +1129,7 @@ async fn classify(
|
|||
Ok(payload) => Outcome::Ran {
|
||||
sent,
|
||||
reply: sink.run(payload, run_deadline(sent)).await,
|
||||
failures_only,
|
||||
},
|
||||
Err(e) => Outcome::NotRun(format!("encoding the run failed: {e}")),
|
||||
};
|
||||
|
|
@ -1170,6 +1177,7 @@ fn review_for(outcome: &Outcome) -> Ruling {
|
|||
Outcome::Ran {
|
||||
sent,
|
||||
reply: Reply::Done { rows, overall },
|
||||
failures_only,
|
||||
} => {
|
||||
let unanswered = rows.iter().filter(|r| r.error.is_some()).count();
|
||||
if rows.is_empty() || rows.len() != *sent {
|
||||
|
|
@ -1191,7 +1199,15 @@ fn review_for(outcome: &Outcome) -> Ruling {
|
|||
};
|
||||
Ruling {
|
||||
event,
|
||||
body: format!("{}\n{}", table(rows), overall_line(*overall)),
|
||||
body: format!(
|
||||
"{}\n{}",
|
||||
if *failures_only {
|
||||
failures(rows)
|
||||
} else {
|
||||
table(rows)
|
||||
},
|
||||
overall_line(*overall)
|
||||
),
|
||||
reason: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1235,6 +1251,21 @@ fn table(rows: &[Row]) -> String {
|
|||
out
|
||||
}
|
||||
|
||||
/// The result table of only the rows that failed their rule, the ones that
|
||||
/// make the review ask for changes, or a line saying none did.
|
||||
fn failures(rows: &[Row]) -> String {
|
||||
let failed: Vec<Row> = rows
|
||||
.iter()
|
||||
.filter(|r| r.counted && !r.passed)
|
||||
.cloned()
|
||||
.collect();
|
||||
if failed.is_empty() {
|
||||
"No check failed.\n".to_owned()
|
||||
} else {
|
||||
table(&failed)
|
||||
}
|
||||
}
|
||||
|
||||
/// A row's result cell: the symbol for what the row did to the review, then
|
||||
/// the model's verdict. ✅ is a counted row that passed its polarity, ❌ a
|
||||
/// counted row that failed it, ❔ a row that doesn't count: below its
|
||||
|
|
@ -1456,6 +1487,54 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
fn done(failures_only: bool, rows: Vec<Row>, overall: Overall) -> Ruling {
|
||||
review_for(&Outcome::Ran {
|
||||
sent: rows.len(),
|
||||
failures_only,
|
||||
reply: Reply::Done { rows, overall },
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failures_only_lists_just_the_rows_that_failed() {
|
||||
let mut failed = row(None, true, false);
|
||||
failed.name = "no-secrets".to_owned();
|
||||
let rows = vec![row(None, true, true), failed, row(None, false, true)];
|
||||
|
||||
let all = done(false, rows.clone(), Overall::RequestChanges);
|
||||
assert!(all.body.contains("datetime"), "{}", all.body);
|
||||
assert!(all.body.contains("no-secrets"), "{}", all.body);
|
||||
|
||||
let only = done(true, rows, Overall::RequestChanges);
|
||||
assert_eq!(only.event, Event::RequestChanges);
|
||||
assert!(only.body.contains("no-secrets"), "{}", only.body);
|
||||
assert!(!only.body.contains("datetime"), "{}", only.body);
|
||||
assert!(only.body.ends_with("**Overall: ❌ request changes**\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failures_only_on_an_approval_says_no_check_failed() {
|
||||
let ruling = done(
|
||||
true,
|
||||
vec![row(None, true, true), row(None, false, true)],
|
||||
Overall::Approve,
|
||||
);
|
||||
assert_eq!(ruling.event, Event::Approve);
|
||||
assert_eq!(ruling.body, "No check failed.\n\n**Overall: ✅ approve**\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failures_only_keeps_the_full_table_when_a_row_got_no_answer() {
|
||||
let ruling = done(
|
||||
true,
|
||||
vec![row(None, true, true), row(Some("timed out"), false, false)],
|
||||
Overall::Approve,
|
||||
);
|
||||
assert_eq!(ruling.event, Event::Comment);
|
||||
assert!(ruling.body.contains("datetime"), "{}", ruling.body);
|
||||
assert!(ruling.body.contains("timed out"), "{}", ruling.body);
|
||||
}
|
||||
|
||||
fn request(name: &str) -> Requested {
|
||||
Requested {
|
||||
owner: "o".to_owned(),
|
||||
|
|
@ -1568,6 +1647,7 @@ mod tests {
|
|||
// empty answer to one still has no verdict.
|
||||
let ruling = review_for(&Outcome::Ran {
|
||||
sent: 0,
|
||||
failures_only: false,
|
||||
reply: Reply::Done {
|
||||
rows: Vec::new(),
|
||||
overall: Overall::Approve,
|
||||
|
|
@ -2727,6 +2807,7 @@ mod tests {
|
|||
effort: Some(Effort::Medium),
|
||||
max_concurrent_reviews: 2,
|
||||
max_classifications: 10,
|
||||
failures_only: false,
|
||||
runtime: super::super::settings::RuntimeKind::Acp,
|
||||
acp: Some(acp.clone()),
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue