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
|
|
@ -7,6 +7,12 @@
|
|||
//! Each agent's harness applies them to its own todo list. The flows are the
|
||||
//! built-in ones, changed by the `notify-flows` document ([`stored`]).
|
||||
//!
|
||||
//! A `request_reviewer` node is a request to swarm-controller, which holds the
|
||||
//! forge token (`swarm_wire::router_sink`). It is sent once, after the event's
|
||||
//! ops are queued and its snapshot is written, and the answer is only logged:
|
||||
//! a controller that is down or slow must not hold up every agent's todos, and
|
||||
//! the next push asks again.
|
||||
//!
|
||||
//! Per issue it keeps a snapshot in its state bucket ([`event::Snapshot`]):
|
||||
//! the assignees it last saw and the agents it queued a todo for. An event is
|
||||
//! acked once its ops are queued and the snapshot is written, so a crash
|
||||
|
|
@ -26,6 +32,7 @@ use async_nats::jetstream::{self, consumer::pull, kv, message::PublishMessage};
|
|||
use futures_util::{StreamExt as _, TryStreamExt as _};
|
||||
use swarm_queue_client::{ClientSecret, QueueConfig};
|
||||
use swarm_secret_client::stored_secret::StoredSecret;
|
||||
use swarm_wire::router_sink::{self, Outcome, RequestReviewer};
|
||||
use swarm_wire::{forge_events, notify_router, router_todo};
|
||||
|
||||
mod event;
|
||||
|
|
@ -103,6 +110,7 @@ async fn main() -> Result<()> {
|
|||
|
||||
struct Router {
|
||||
js: jetstream::Context,
|
||||
nats: async_nats::Client,
|
||||
events: jetstream::consumer::Consumer<pull::Config>,
|
||||
state: kv::Store,
|
||||
flows: stored::Flows,
|
||||
|
|
@ -144,6 +152,7 @@ impl Router {
|
|||
};
|
||||
Ok(Self {
|
||||
js,
|
||||
nats: queue.clone(),
|
||||
events,
|
||||
state,
|
||||
flows: stored::Flows::builtins(),
|
||||
|
|
@ -200,9 +209,52 @@ impl Router {
|
|||
let bytes = serde_json::to_vec(&routed.next).expect("a snapshot encodes");
|
||||
self.state.put(&key, bytes.into()).await.map_err(retry)?;
|
||||
}
|
||||
for request in &routed.requests {
|
||||
self.request_review(request).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ask swarm-controller for one review and log what it answered. One
|
||||
/// attempt: nothing here fails the event.
|
||||
async fn request_review(&self, request: &flow::ReviewRequest) {
|
||||
let wire = RequestReviewer {
|
||||
repo: request.repo.clone(),
|
||||
number: request.number,
|
||||
reviewer: request.reviewer.clone(),
|
||||
};
|
||||
let body = serde_json::to_vec(&wire).expect("a review request encodes");
|
||||
let sent = async_nats::Request::new()
|
||||
.payload(body.into())
|
||||
.timeout(Some(router_sink::REPLY_DEADLINE));
|
||||
let (flow, repo, number, reviewer) = (
|
||||
&request.flow,
|
||||
&request.repo,
|
||||
request.number,
|
||||
&request.reviewer,
|
||||
);
|
||||
match self
|
||||
.nats
|
||||
.send_request(router_sink::REQUEST_REVIEWER_SUBJECT, sent)
|
||||
.await
|
||||
{
|
||||
Ok(reply) => match Outcome::decode(&String::from_utf8_lossy(&reply.payload)) {
|
||||
Outcome::Requested => {
|
||||
tracing::info!(%flow, %repo, number, %reviewer, "notify router: review requested");
|
||||
}
|
||||
Outcome::Skipped(why) => {
|
||||
tracing::info!(%flow, %repo, number, %reviewer, %why, "notify router: review not requested");
|
||||
}
|
||||
Outcome::Error(why) => {
|
||||
tracing::warn!(%flow, %repo, number, %reviewer, %why, "notify router: the controller could not request the review");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::warn!(%flow, %repo, number, %reviewer, error = %e, "notify router: no answer to a review request; not retried");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Queue one op on its agent's subject and wait for the stream's ack.
|
||||
async fn queue(&self, seq: u64, action: &flow::Action) -> Result<()> {
|
||||
let subject = router_todo::subject(&action.agent);
|
||||
|
|
|
|||
Loading…
Reference in a new issue