request the review classifier's review when a PR's head moves
The router gets a request_reviewer node and a pull_request_sync source; the built-in head-classifier flow asks swarm-controller, over $SWARM.router.sink.request-reviewer, to request review-classifier on every push, unless its review of the head already stands. The controller subscribes to pull_request_sync on its pull-request hook and answers the router; nats-auth grants the router the publish and the controller the subscribe.
This commit is contained in:
parent
463c5cbfcc
commit
0b1669a364
20 changed files with 1231 additions and 64 deletions
|
|
@ -8,13 +8,16 @@ use swarm_wire::router_todo::Op;
|
|||
use crate::event::{Delivery, Snapshot, fields, is_assign};
|
||||
use swarm_wire::notify_flows::Flow;
|
||||
|
||||
use crate::flow::{Action, evaluate};
|
||||
use crate::flow::{Action, ReviewRequest, evaluate, requests};
|
||||
|
||||
/// What one event comes to.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Routed {
|
||||
/// The ops to queue, one per agent that exists.
|
||||
pub actions: Vec<Action>,
|
||||
/// The reviews to ask the forge for. Not limited to agents: a reviewer is
|
||||
/// a forge account.
|
||||
pub requests: Vec<ReviewRequest>,
|
||||
/// Recipients that are not agents of this swarm, so got nothing.
|
||||
pub skipped: Vec<String>,
|
||||
pub next: Snapshot,
|
||||
|
|
@ -32,6 +35,10 @@ pub fn route(
|
|||
let fields = fields(event_type, delivery, snapshot);
|
||||
let mut actions = Vec::new();
|
||||
let mut skipped = Vec::new();
|
||||
let mut review_requests = Vec::new();
|
||||
for flow in flows {
|
||||
review_requests.extend(requests(flow, event_type, &fields));
|
||||
}
|
||||
for action in flows.iter().flat_map(|f| evaluate(f, event_type, &fields)) {
|
||||
if is_agent(&action.agent) {
|
||||
actions.push(action);
|
||||
|
|
@ -62,6 +69,7 @@ pub fn route(
|
|||
next.holders.retain(|h| !cleared.contains(h));
|
||||
Routed {
|
||||
actions,
|
||||
requests: review_requests,
|
||||
skipped,
|
||||
next,
|
||||
}
|
||||
|
|
@ -186,6 +194,44 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// A push to a PR makes one request for the classifier's review, whether
|
||||
/// or not the classifier is an agent, and touches nothing else.
|
||||
#[test]
|
||||
fn a_push_to_a_pull_request_requests_the_classifiers_review_once() {
|
||||
let flows = builtins();
|
||||
let pull = Delivery {
|
||||
is_pull: true,
|
||||
..delivery("synchronized", &[])
|
||||
};
|
||||
let before = Snapshot {
|
||||
assignees: Some(BTreeSet::from(["atlas".to_owned()])),
|
||||
holders: BTreeSet::from(["atlas".to_owned()]),
|
||||
};
|
||||
let r = route("pull_request_sync", &pull, &before, &flows, |_| false);
|
||||
assert_eq!(
|
||||
r.requests,
|
||||
[ReviewRequest {
|
||||
flow: "head-classifier".to_owned(),
|
||||
node: "request".to_owned(),
|
||||
repo: "o/r".to_owned(),
|
||||
number: 9,
|
||||
reviewer: "review-classifier".to_owned(),
|
||||
}]
|
||||
);
|
||||
assert!(r.actions.is_empty() && r.skipped.is_empty());
|
||||
assert_eq!(r.next, before);
|
||||
|
||||
// The source gates: another PR event requests nothing.
|
||||
for other in ["pull_request", "pull_request_assign", "issues"] {
|
||||
let r = route(other, &pull, &before, &flows, |_| true);
|
||||
assert!(r.requests.is_empty(), "{other}");
|
||||
}
|
||||
// Only a PR can be asked to review.
|
||||
let issue = delivery("synchronized", &[]);
|
||||
let r = route("pull_request_sync", &issue, &before, &flows, |_| true);
|
||||
assert!(r.requests.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_event_no_flow_matches_changes_nothing() {
|
||||
let before = Snapshot {
|
||||
|
|
|
|||
Loading…
Reference in a new issue