forked from kemitix/git-next
166 lines
5 KiB
Rust
166 lines
5 KiB
Rust
|
//
|
||
|
use actix::prelude::*;
|
||
|
|
||
|
use derive_more::Deref;
|
||
|
use kxio::network::Network;
|
||
|
use messages::NotifyUser;
|
||
|
use std::time::Duration;
|
||
|
use tracing::{info, warn, Instrument};
|
||
|
|
||
|
use git_next_core::{
|
||
|
git::{
|
||
|
self,
|
||
|
repository::{factory::RepositoryFactory, open::OpenRepositoryLike},
|
||
|
UserNotification,
|
||
|
},
|
||
|
server, WebhookAuth, WebhookId,
|
||
|
};
|
||
|
|
||
|
mod branch;
|
||
|
pub mod handlers;
|
||
|
mod load;
|
||
|
pub mod messages;
|
||
|
mod notifications;
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests;
|
||
|
|
||
|
#[derive(Clone, Debug, Default)]
|
||
|
pub struct RepoActorLog(std::sync::Arc<std::sync::RwLock<Vec<String>>>);
|
||
|
impl Deref for RepoActorLog {
|
||
|
type Target = std::sync::Arc<std::sync::RwLock<Vec<String>>>;
|
||
|
|
||
|
fn deref(&self) -> &Self::Target {
|
||
|
&self.0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// An actor that represents a Git Repository.
|
||
|
///
|
||
|
/// When this actor is started it is sent the [CloneRepo] message.
|
||
|
#[derive(Debug, derive_more::Display, derive_with::With)]
|
||
|
#[display("{}:{}:{}", generation, repo_details.forge.forge_alias(), repo_details.repo_alias)]
|
||
|
pub struct RepoActor {
|
||
|
sleep_duration: std::time::Duration,
|
||
|
generation: git::Generation,
|
||
|
message_token: messages::MessageToken,
|
||
|
repo_details: git::RepoDetails,
|
||
|
webhook: server::InboundWebhook,
|
||
|
webhook_id: Option<WebhookId>, // INFO: if [None] then no webhook is configured
|
||
|
webhook_auth: Option<WebhookAuth>, // INFO: if [None] then no webhook is configured
|
||
|
last_main_commit: Option<git::Commit>,
|
||
|
last_next_commit: Option<git::Commit>,
|
||
|
last_dev_commit: Option<git::Commit>,
|
||
|
repository_factory: Box<dyn RepositoryFactory>,
|
||
|
open_repository: Option<Box<dyn OpenRepositoryLike>>,
|
||
|
net: Network,
|
||
|
forge: Box<dyn git::ForgeLike>,
|
||
|
log: Option<RepoActorLog>,
|
||
|
notify_user_recipient: Option<Recipient<NotifyUser>>,
|
||
|
}
|
||
|
impl RepoActor {
|
||
|
#[allow(clippy::too_many_arguments)]
|
||
|
pub fn new(
|
||
|
repo_details: git::RepoDetails,
|
||
|
forge: Box<dyn git::ForgeLike>,
|
||
|
webhook: server::InboundWebhook,
|
||
|
generation: git::Generation,
|
||
|
net: Network,
|
||
|
repository_factory: Box<dyn RepositoryFactory>,
|
||
|
sleep_duration: std::time::Duration,
|
||
|
notify_user_recipient: Option<Recipient<NotifyUser>>,
|
||
|
) -> Self {
|
||
|
let message_token = messages::MessageToken::default();
|
||
|
Self {
|
||
|
generation,
|
||
|
message_token,
|
||
|
repo_details,
|
||
|
webhook,
|
||
|
webhook_id: None,
|
||
|
webhook_auth: None,
|
||
|
last_main_commit: None,
|
||
|
last_next_commit: None,
|
||
|
last_dev_commit: None,
|
||
|
repository_factory,
|
||
|
open_repository: None,
|
||
|
forge,
|
||
|
net,
|
||
|
sleep_duration,
|
||
|
log: None,
|
||
|
notify_user_recipient,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
impl Actor for RepoActor {
|
||
|
type Context = Context<Self>;
|
||
|
#[tracing::instrument(name = "RepoActor::stopping", skip_all, fields(repo = %self.repo_details))]
|
||
|
fn stopping(&mut self, ctx: &mut Self::Context) -> Running {
|
||
|
tracing::debug!("stopping");
|
||
|
info!("Checking webhook");
|
||
|
match self.webhook_id.take() {
|
||
|
Some(webhook_id) => {
|
||
|
tracing::warn!("stopping - unregistering webhook");
|
||
|
info!(%webhook_id, "Unregistring webhook");
|
||
|
let forge = self.forge.duplicate();
|
||
|
async move {
|
||
|
if let Err(err) = forge.unregister_webhook(&webhook_id).await {
|
||
|
warn!("unregistering webhook: {err}");
|
||
|
}
|
||
|
}
|
||
|
.in_current_span()
|
||
|
.into_actor(self)
|
||
|
.wait(ctx);
|
||
|
Running::Continue
|
||
|
}
|
||
|
None => Running::Stop,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn delay_send<M>(addr: Addr<RepoActor>, delay: Duration, msg: M, log: Option<&RepoActorLog>)
|
||
|
where
|
||
|
M: actix::Message + Send + 'static + std::fmt::Debug,
|
||
|
RepoActor: actix::Handler<M>,
|
||
|
<M as actix::Message>::Result: Send,
|
||
|
{
|
||
|
let log_message = format!("send-after-delay: {:?}", msg);
|
||
|
tracing::debug!(log_message);
|
||
|
logger(log, log_message);
|
||
|
std::thread::sleep(delay);
|
||
|
do_send(addr, msg, log)
|
||
|
}
|
||
|
|
||
|
pub fn do_send<M>(_addr: Addr<RepoActor>, msg: M, log: Option<&RepoActorLog>)
|
||
|
where
|
||
|
M: actix::Message + Send + 'static + std::fmt::Debug,
|
||
|
RepoActor: actix::Handler<M>,
|
||
|
<M as actix::Message>::Result: Send,
|
||
|
{
|
||
|
let log_message = format!("send: {:?}", msg);
|
||
|
tracing::debug!(log_message);
|
||
|
logger(log, log_message);
|
||
|
#[cfg(not(test))]
|
||
|
_addr.do_send(msg)
|
||
|
}
|
||
|
|
||
|
pub fn logger(log: Option<&RepoActorLog>, message: impl Into<String>) {
|
||
|
if let Some(log) = log {
|
||
|
let message: String = message.into();
|
||
|
tracing::debug!(message);
|
||
|
let _ = log.write().map(|mut l| l.push(message));
|
||
|
}
|
||
|
}
|
||
|
pub fn notify_user(
|
||
|
recipient: Option<&Recipient<NotifyUser>>,
|
||
|
user_notification: UserNotification,
|
||
|
log: Option<&RepoActorLog>,
|
||
|
) {
|
||
|
let msg = NotifyUser::from(user_notification);
|
||
|
let log_message = format!("send: {:?}", msg);
|
||
|
tracing::debug!(log_message);
|
||
|
logger(log, log_message);
|
||
|
if let Some(recipient) = &recipient {
|
||
|
recipient.do_send(msg);
|
||
|
}
|
||
|
}
|