git-next/crates/repo-actor/src/lib.rs

182 lines
5.2 KiB
Rust
Raw Normal View History

mod branch;
pub mod handlers;
mod load;
pub mod messages;
2024-04-09 10:44:01 +01:00
2024-05-14 07:59:31 +01:00
#[cfg(test)]
mod tests;
2024-04-09 10:44:01 +01:00
use actix::prelude::*;
2024-05-23 16:50:36 +01:00
use git_next_config as config;
use git_next_git as git;
2024-05-23 16:50:36 +01:00
2024-04-09 10:44:01 +01:00
use kxio::network::Network;
use tracing::{info, warn, Instrument};
pub type RepoActorLog = std::sync::Arc<std::sync::Mutex<Vec<String>>>;
/// An actor that represents a Git Repository.
///
/// When this actor is started it is sent the [CloneRepo] message.
///
/// ```mermaid
/// stateDiagram-v2
/// [*] --> CloneRepo :START
///
/// CloneRepo --> LoadConfigFromRepo
/// CloneRepo --> ValidateRepo
///
/// LoadConfigFromRepo --> LoadedConfig
///
/// ValidateRepo --> WebhookRegistered
/// ValidateRepo --> StartMonitoring
/// ValidateRepo --> ValidateRepo :SLEEP 10s
///
/// LoadedConfig --> ValidateRepo
///
/// WebhookRegistered --> [*]
///
/// StartMonitoring --> AdvanceMainTo
/// StartMonitoring --> ValidateRepo :SLEEP 10s
///
/// AdvanceMainTo --> LoadConfigFromRepo
/// AdvanceMainTo --> ValidateRepo
///
/// [*] --> WebhookMessage :WEBHOOK
///
/// WebhookMessage --> ValidateRepo
/// ```
///
#[derive(Debug, derive_more::Display)]
#[display("{}:{}:{}", generation, repo_details.forge.forge_alias(), repo_details.repo_alias)]
2024-04-09 10:44:01 +01:00
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<config::WebhookId>, // INFO: if [None] then no webhook is configured
webhook_auth: Option<config::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 git::repository::RepositoryFactory>,
open_repository: Option<Box<dyn git::repository::OpenRepositoryLike>>,
2024-04-09 10:44:01 +01:00
net: Network,
forge: Box<dyn git::ForgeLike>,
log: Option<RepoActorLog>,
2024-04-09 10:44:01 +01:00
}
impl RepoActor {
pub fn new(
repo_details: git::RepoDetails,
forge: Box<dyn git::ForgeLike>,
webhook: config::server::Webhook,
generation: git::Generation,
net: Network,
repository_factory: Box<dyn git::repository::RepositoryFactory>,
sleep_duration: std::time::Duration,
) -> Self {
let message_token = messages::MessageToken::default();
2024-04-09 10:44:01 +01:00
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,
2024-04-09 10:44:01 +01:00
}
}
}
#[cfg(test)]
impl RepoActor {
pub fn with_log(mut self, log: RepoActorLog) -> Self {
self.log = Some(log);
self
}
pub fn with_open_repository(
mut self,
open_repository: Box<dyn git::repository::OpenRepositoryLike>,
) -> Self {
self.open_repository.replace(open_repository);
self
}
pub const fn with_message_token(mut self, message_token: messages::MessageToken) -> Self {
self.message_token = message_token;
self
}
}
2024-04-09 10:44:01 +01:00
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,
}
}
2024-04-09 10:44:01 +01:00
}
pub fn do_send<M>(_addr: Addr<RepoActor>, msg: M, log: &Option<crate::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 async fn send<M>(
addr: Addr<RepoActor>,
msg: M,
log: &Option<crate::RepoActorLog>,
) -> std::result::Result<<M as actix::Message>::Result, actix::MailboxError>
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);
logger(log, log_message);
addr.send(msg).await
}
pub fn logger(log: &Option<crate::RepoActorLog>, message: impl Into<String>) {
if let Some(log) = log {
let message: String = message.into();
tracing::debug!(message);
let _ = log.lock().map(|mut l| l.push(message));
}
}