Watch
0
0
Fork
You've already forked hyperhive
0

review-classifier: skip draft prs, classify once when they leave draft

This commit is contained in:
atlas 2026-11-02 17:58:22 +01:00 • committed by mara
commit 49ef93a19e

View file

@ -87,6 +87,14 @@ pub const FIELDS: &[(&str, &str)] = &[
("title", "The issue or pull request title"),
("url", "The web link to the issue or pull request"),
("sender", "The account that caused the event"),
(
"draft",
"true when the pull request is a draft (its title starts WIP: or [WIP]), else false",
),
(
"undrafted",
"true when the event is a title edit that took the pull request out of draft, else false",
),
("assignees", "The agents assigned after the event (a list)"),
("assignees_added", "The agents the event assigned (a list)"),
(
@ -839,8 +847,16 @@ fn closed() -> Flow {
}
/// source(`pull_request`, `pull_request_sync`) → match(`action` = `opened`,
/// `reopened` or `synchronized`) → request the review classifier's review.
/// `reopened` or `synchronized`, and `draft` = `false`) → request the review
/// classifier's review; and match(`undrafted` = `true`) → the same request,
/// for a PR whose last push came while it was a draft.
fn head_classifier() -> Flow {
let branches = [
("opened", "action", "opened"),
("reopened", "action", "reopened"),
("synchronized", "action", "synchronized"),
("undrafted", "undrafted", "true"),
];
let mut nodes = vec![Node {
id: "source".to_owned(),
kind: NodeKind::Source {
@ -848,16 +864,19 @@ fn head_classifier() -> Flow {
},
}];
let mut edges = Vec::new();
for action in ["opened", "reopened", "synchronized"] {
let (matched, request) = (format!("match-{action}"), format!("request-{action}"));
for (name, field, equals) in branches {
let (matched, request) = (format!("match-{name}"), format!("request-{name}"));
let condition = |field: &str, equals: &str| Condition {
field: field.to_owned(),
equals: equals.to_owned(),
};
let mut when = vec![condition(field, equals)];
if field == "action" {
when.push(condition("draft", "false"));
}
nodes.push(Node {
id: matched.clone(),
kind: NodeKind::Match {
when: vec![Condition {
field: "action".to_owned(),
equals: action.to_owned(),
}],
},
kind: NodeKind::Match { when },
});
nodes.push(Node {
id: request.clone(),
@ -1238,6 +1257,10 @@ mod tests {
fn head_classifier_requests_the_classifier_on_open_reopen_and_push() {
let f = builtin("head-classifier");
assert_eq!(f.validate(), Ok(()));
let cond = |field: &str, equals: &str| Condition {
field: field.to_owned(),
equals: equals.to_owned(),
};
assert_eq!(
f.nodes[0].kind,
NodeKind::Source {
@ -1246,17 +1269,22 @@ mod tests {
);
assert_eq!(
f.children("source"),
["match-opened", "match-reopened", "match-synchronized"]
[
"match-opened",
"match-reopened",
"match-synchronized",
"match-undrafted"
]
);
for action in ["opened", "reopened", "synchronized"] {
for action in ["opened", "reopened", "synchronized", "undrafted"] {
let when = if action == "undrafted" {
vec![cond("undrafted", "true")]
} else {
vec![cond("action", action), cond("draft", "false")]
};
assert_eq!(
f.node(&format!("match-{action}")).map(|n| &n.kind),
Some(&NodeKind::Match {
when: vec![Condition {
field: "action".to_owned(),
equals: action.to_owned()
}]
})
Some(&NodeKind::Match { when })
);
assert_eq!(
f.node(&format!("request-{action}")).map(|n| &n.kind),