// 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>>); impl Deref for RepoActorLog { type Target = std::sync::Arc>>; 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, // INFO: if [None] then no webhook is configured webhook_auth: Option, // INFO: if [None] then no webhook is configured last_main_commit: Option, last_next_commit: Option, last_dev_commit: Option, repository_factory: Box, open_repository: Option>, net: Network, forge: Box, log: Option, notify_user_recipient: Option>, } impl RepoActor { #[allow(clippy::too_many_arguments)] pub fn new( repo_details: git::RepoDetails, forge: Box, webhook: server::InboundWebhook, generation: git::Generation, net: Network, repository_factory: Box, sleep_duration: std::time::Duration, notify_user_recipient: Option>, ) -> 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; #[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(addr: Addr, delay: Duration, msg: M, log: Option<&RepoActorLog>) where M: actix::Message + Send + 'static + std::fmt::Debug, RepoActor: actix::Handler, ::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(_addr: Addr, msg: M, log: Option<&RepoActorLog>) where M: actix::Message + Send + 'static + std::fmt::Debug, RepoActor: actix::Handler, ::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) { 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>, 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); } }