mod branch; pub mod handlers; mod load; pub mod messages; #[cfg(test)] mod tests; use std::time::Duration; use actix::prelude::*; use derive_more::Deref; use git_next_config as config; use git_next_git as git; use kxio::network::Network; use tracing::{info, warn, Instrument}; #[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: config::server::Webhook, 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, } impl RepoActor { pub fn new( repo_details: git::RepoDetails, forge: Box, webhook: config::server::Webhook, generation: git::Generation, net: Network, repository_factory: Box, sleep_duration: std::time::Duration, ) -> 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, } } } 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, ) 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) 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, 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)); } }