2024-04-09 15:31:59 +01:00
|
|
|
mod branch;
|
2024-05-22 08:41:30 +01:00
|
|
|
mod load;
|
2024-04-10 09:16:42 +01:00
|
|
|
pub mod status;
|
2024-04-14 19:12:51 +01:00
|
|
|
pub mod webhook;
|
2024-04-09 10:44:01 +01:00
|
|
|
|
2024-05-14 07:59:31 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2024-05-22 08:41:30 +01:00
|
|
|
use std::time::Duration;
|
|
|
|
|
2024-04-09 10:44:01 +01:00
|
|
|
use actix::prelude::*;
|
2024-05-26 08:56:01 +01:00
|
|
|
use git::validation::positions::{validate_positions, Positions};
|
2024-05-23 16:50:36 +01:00
|
|
|
|
2024-05-23 17:53:36 +01:00
|
|
|
use crate as repo_actor;
|
2024-05-23 17:56:47 +01:00
|
|
|
use git_next_config as config;
|
2024-05-23 16:04:38 +01:00
|
|
|
use git_next_forge as forge;
|
2024-05-23 16:19:28 +01:00
|
|
|
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;
|
2024-05-04 12:37:35 +01:00
|
|
|
use tracing::{debug, info, warn, Instrument};
|
2024-04-09 10:44:01 +01:00
|
|
|
|
2024-05-15 21:01:19 +01:00
|
|
|
#[derive(Debug, derive_more::Display)]
|
2024-05-26 08:35:45 +01:00
|
|
|
#[display("{}:{}:{}", generation, repo_details.forge.forge_name(), repo_details.repo_alias)]
|
2024-04-09 10:44:01 +01:00
|
|
|
pub struct RepoActor {
|
2024-05-23 16:19:28 +01:00
|
|
|
generation: git::Generation,
|
2024-05-23 08:30:58 +01:00
|
|
|
message_token: MessageToken,
|
2024-05-26 08:35:45 +01:00
|
|
|
repo_details: git::RepoDetails,
|
2024-05-23 17:56:47 +01:00
|
|
|
webhook: config::server::Webhook,
|
2024-05-23 16:50:36 +01:00
|
|
|
webhook_id: Option<webhook::WebhookId>, // INFO: if [None] then no webhook is configured
|
|
|
|
webhook_auth: Option<webhook::WebhookAuth>, // INFO: if [None] then no webhook is configured
|
2024-05-11 19:46:20 +01:00
|
|
|
last_main_commit: Option<git::Commit>,
|
|
|
|
last_next_commit: Option<git::Commit>,
|
|
|
|
last_dev_commit: Option<git::Commit>,
|
2024-05-23 16:50:36 +01:00
|
|
|
repository: Option<git::OpenRepository>,
|
2024-04-09 10:44:01 +01:00
|
|
|
net: Network,
|
2024-05-23 16:04:38 +01:00
|
|
|
forge: forge::Forge,
|
2024-04-09 10:44:01 +01:00
|
|
|
}
|
|
|
|
impl RepoActor {
|
2024-05-11 19:46:20 +01:00
|
|
|
pub fn new(
|
2024-05-23 16:19:28 +01:00
|
|
|
details: git::RepoDetails,
|
2024-05-23 17:56:47 +01:00
|
|
|
webhook: config::server::Webhook,
|
2024-05-23 16:19:28 +01:00
|
|
|
generation: git::Generation,
|
2024-05-07 08:17:29 +01:00
|
|
|
net: Network,
|
2024-05-18 11:41:18 +01:00
|
|
|
repo: git::Repository,
|
2024-05-07 08:17:29 +01:00
|
|
|
) -> Self {
|
2024-05-15 07:55:05 +01:00
|
|
|
let forge = match details.forge.forge_type() {
|
2024-04-16 22:21:55 +01:00
|
|
|
#[cfg(feature = "forgejo")]
|
2024-05-23 17:56:47 +01:00
|
|
|
config::ForgeType::ForgeJo => {
|
2024-05-23 16:50:36 +01:00
|
|
|
forge::Forge::new_forgejo(details.clone(), net.clone(), repo)
|
|
|
|
}
|
2024-05-23 17:56:47 +01:00
|
|
|
config::ForgeType::MockForge => forge::Forge::new_mock(),
|
2024-04-16 22:21:55 +01:00
|
|
|
};
|
2024-05-04 12:37:35 +01:00
|
|
|
debug!(?forge, "new");
|
2024-04-09 10:44:01 +01:00
|
|
|
Self {
|
2024-05-07 08:17:29 +01:00
|
|
|
generation,
|
2024-05-23 08:30:58 +01:00
|
|
|
message_token: MessageToken::new(),
|
2024-05-26 08:35:45 +01:00
|
|
|
repo_details: details,
|
2024-04-14 06:48:26 +01:00
|
|
|
webhook,
|
2024-04-09 19:30:05 +01:00
|
|
|
webhook_id: None,
|
2024-04-14 06:48:26 +01:00
|
|
|
webhook_auth: None,
|
2024-04-14 15:46:21 +01:00
|
|
|
last_main_commit: None,
|
|
|
|
last_next_commit: None,
|
|
|
|
last_dev_commit: None,
|
2024-05-09 21:18:40 +01:00
|
|
|
repository: None,
|
2024-04-09 10:44:01 +01:00
|
|
|
net,
|
2024-04-16 22:21:55 +01:00
|
|
|
forge,
|
2024-04-09 10:44:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl Actor for RepoActor {
|
|
|
|
type Context = Context<Self>;
|
2024-05-26 08:35:45 +01:00
|
|
|
#[tracing::instrument(name = "RepoActor::stopping", skip_all, fields(repo = %self.repo_details))]
|
2024-04-09 19:30:05 +01:00
|
|
|
fn stopping(&mut self, ctx: &mut Self::Context) -> Running {
|
2024-05-04 12:37:35 +01:00
|
|
|
info!("Checking webhook");
|
2024-04-09 19:30:05 +01:00
|
|
|
match self.webhook_id.take() {
|
|
|
|
Some(webhook_id) => {
|
2024-05-26 08:35:45 +01:00
|
|
|
let repo_details = self.repo_details.clone();
|
2024-04-09 19:30:05 +01:00
|
|
|
let net = self.net.clone();
|
2024-05-07 08:17:29 +01:00
|
|
|
info!(%webhook_id, "Unregistring webhook");
|
2024-04-13 20:59:57 +01:00
|
|
|
webhook::unregister(webhook_id, repo_details, net)
|
2024-05-04 12:37:35 +01:00
|
|
|
.in_current_span()
|
2024-04-09 19:30:05 +01:00
|
|
|
.into_actor(self)
|
|
|
|
.wait(ctx);
|
|
|
|
Running::Continue
|
|
|
|
}
|
|
|
|
None => Running::Stop,
|
|
|
|
}
|
|
|
|
}
|
2024-04-09 10:44:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Message)]
|
|
|
|
#[rtype(result = "()")]
|
2024-04-23 07:09:30 +01:00
|
|
|
pub struct CloneRepo;
|
|
|
|
impl Handler<CloneRepo> for RepoActor {
|
2024-04-09 10:44:01 +01:00
|
|
|
type Result = ();
|
2024-05-26 08:35:45 +01:00
|
|
|
#[tracing::instrument(name = "RepoActor::CloneRepo", skip_all, fields(repo = %self.repo_details, gitdir = %self.repo_details.gitdir))]
|
2024-04-23 07:09:30 +01:00
|
|
|
fn handle(&mut self, _msg: CloneRepo, ctx: &mut Self::Context) -> Self::Result {
|
2024-05-26 08:35:45 +01:00
|
|
|
let gitdir = self.repo_details.gitdir.clone();
|
2024-04-23 07:09:30 +01:00
|
|
|
match self.forge.repo_clone(gitdir) {
|
2024-05-09 21:18:40 +01:00
|
|
|
Ok(repository) => {
|
|
|
|
self.repository.replace(repository);
|
2024-05-26 08:35:45 +01:00
|
|
|
if self.repo_details.repo_config.is_none() {
|
2024-05-08 07:14:14 +01:00
|
|
|
ctx.address().do_send(LoadConfigFromRepo);
|
|
|
|
} else {
|
|
|
|
ctx.address().do_send(ValidateRepo {
|
|
|
|
message_token: self.message_token,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2024-05-03 17:50:50 +01:00
|
|
|
Err(err) => warn!("Could not Clone repo: {err}"),
|
2024-04-23 07:09:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Message)]
|
|
|
|
#[rtype(result = "()")]
|
|
|
|
pub struct LoadConfigFromRepo;
|
|
|
|
impl Handler<LoadConfigFromRepo> for RepoActor {
|
|
|
|
type Result = ();
|
2024-05-26 08:35:45 +01:00
|
|
|
#[tracing::instrument(name = "RepoActor::LoadConfigFromRepo", skip_all, fields(repo = %self.repo_details))]
|
2024-04-23 07:09:30 +01:00
|
|
|
fn handle(&mut self, _msg: LoadConfigFromRepo, ctx: &mut Self::Context) -> Self::Result {
|
2024-05-26 08:35:45 +01:00
|
|
|
let details = self.repo_details.clone();
|
2024-04-09 10:44:01 +01:00
|
|
|
let addr = ctx.address();
|
2024-04-16 22:21:55 +01:00
|
|
|
let forge = self.forge.clone();
|
2024-05-23 17:53:36 +01:00
|
|
|
repo_actor::load::load_file(details, addr, forge)
|
2024-05-04 12:37:35 +01:00
|
|
|
.in_current_span()
|
2024-04-16 22:21:55 +01:00
|
|
|
.into_actor(self)
|
|
|
|
.wait(ctx);
|
2024-04-09 10:44:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Message)]
|
|
|
|
#[rtype(result = "()")]
|
2024-05-23 16:50:36 +01:00
|
|
|
struct LoadedConfig(git_next_config::RepoConfig);
|
2024-04-09 10:44:01 +01:00
|
|
|
impl Handler<LoadedConfig> for RepoActor {
|
|
|
|
type Result = ();
|
2024-05-26 08:35:45 +01:00
|
|
|
#[tracing::instrument(name = "RepoActor::LoadedConfig", skip_all, fields(repo = %self.repo_details, branches = %msg.0))]
|
2024-04-09 15:31:59 +01:00
|
|
|
fn handle(&mut self, msg: LoadedConfig, ctx: &mut Self::Context) -> Self::Result {
|
2024-04-21 19:33:18 +01:00
|
|
|
let repo_config = msg.0;
|
2024-05-26 08:35:45 +01:00
|
|
|
self.repo_details.repo_config.replace(repo_config);
|
2024-05-08 07:14:14 +01:00
|
|
|
|
2024-05-05 08:17:32 +01:00
|
|
|
ctx.address().do_send(ValidateRepo {
|
|
|
|
message_token: self.message_token,
|
|
|
|
});
|
2024-04-11 15:43:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-15 21:01:19 +01:00
|
|
|
#[derive(derive_more::Constructor, Message)]
|
2024-04-11 15:43:02 +01:00
|
|
|
#[rtype(result = "()")]
|
2024-05-05 08:17:32 +01:00
|
|
|
pub struct ValidateRepo {
|
2024-05-23 08:30:58 +01:00
|
|
|
message_token: MessageToken,
|
2024-05-05 08:17:32 +01:00
|
|
|
}
|
2024-04-11 15:43:02 +01:00
|
|
|
impl Handler<ValidateRepo> for RepoActor {
|
|
|
|
type Result = ();
|
2024-05-26 08:35:45 +01:00
|
|
|
#[tracing::instrument(name = "RepoActor::ValidateRepo", skip_all, fields(repo = %self.repo_details, token = %msg.message_token))]
|
2024-05-05 08:17:32 +01:00
|
|
|
fn handle(&mut self, msg: ValidateRepo, ctx: &mut Self::Context) -> Self::Result {
|
|
|
|
match msg.message_token {
|
|
|
|
message_token if self.message_token < message_token => {
|
|
|
|
info!(%message_token, "New message token");
|
|
|
|
self.message_token = msg.message_token;
|
|
|
|
}
|
|
|
|
message_token if self.message_token > message_token => {
|
|
|
|
info!("Dropping message from previous generation");
|
|
|
|
return; // message is expired
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
}
|
2024-05-08 07:14:14 +01:00
|
|
|
if self.webhook_id.is_none() {
|
|
|
|
webhook::register(
|
2024-05-26 08:35:45 +01:00
|
|
|
self.repo_details.clone(),
|
2024-05-08 07:14:14 +01:00
|
|
|
self.webhook.clone(),
|
|
|
|
ctx.address(),
|
|
|
|
self.net.clone(),
|
|
|
|
)
|
|
|
|
.in_current_span()
|
|
|
|
.into_actor(self)
|
|
|
|
.wait(ctx);
|
|
|
|
}
|
2024-05-26 08:35:45 +01:00
|
|
|
if let (Some(repository), Some(repo_config)) = (
|
|
|
|
self.repository.clone(),
|
|
|
|
self.repo_details.repo_config.clone(),
|
|
|
|
) {
|
|
|
|
let repo_details = self.repo_details.clone();
|
2024-04-11 15:43:02 +01:00
|
|
|
let addr = ctx.address();
|
2024-05-05 08:17:32 +01:00
|
|
|
let message_token = self.message_token;
|
|
|
|
async move {
|
2024-05-26 08:56:01 +01:00
|
|
|
match validate_positions(&repository, &repo_details, repo_config) {
|
|
|
|
Ok(Positions {
|
2024-05-22 08:41:30 +01:00
|
|
|
main,
|
|
|
|
next,
|
|
|
|
dev,
|
|
|
|
dev_commit_history,
|
|
|
|
}) => {
|
|
|
|
addr.do_send(StartMonitoring::new(main, next, dev, dev_commit_history));
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
warn!("{:?}", err);
|
|
|
|
tokio::time::sleep(Duration::from_secs(10)).await;
|
|
|
|
addr.do_send(ValidateRepo::new(message_token));
|
|
|
|
}
|
|
|
|
}
|
2024-05-05 08:17:32 +01:00
|
|
|
}
|
|
|
|
.in_current_span()
|
|
|
|
.into_actor(self)
|
|
|
|
.wait(ctx);
|
2024-04-11 15:43:02 +01:00
|
|
|
}
|
2024-04-09 10:44:01 +01:00
|
|
|
}
|
|
|
|
}
|
2024-04-09 18:18:19 +01:00
|
|
|
|
2024-05-15 21:01:19 +01:00
|
|
|
#[derive(Debug, derive_more::Constructor, Message)]
|
2024-04-09 18:18:19 +01:00
|
|
|
#[rtype(result = "()")]
|
|
|
|
pub struct StartMonitoring {
|
2024-05-15 21:01:19 +01:00
|
|
|
main: git::Commit,
|
|
|
|
next: git::Commit,
|
|
|
|
dev: git::Commit,
|
|
|
|
dev_commit_history: Vec<git::Commit>,
|
2024-04-09 18:18:19 +01:00
|
|
|
}
|
|
|
|
impl Handler<StartMonitoring> for RepoActor {
|
|
|
|
type Result = ();
|
2024-05-15 21:01:19 +01:00
|
|
|
#[tracing::instrument(name = "RepoActor::StartMonitoring", skip_all,
|
2024-05-26 08:35:45 +01:00
|
|
|
fields(token = %self.message_token, repo = %self.repo_details, main = %msg.main, next= %msg.next, dev = %msg.dev))
|
2024-05-15 21:01:19 +01:00
|
|
|
]
|
2024-04-09 19:30:05 +01:00
|
|
|
fn handle(&mut self, msg: StartMonitoring, ctx: &mut Self::Context) -> Self::Result {
|
2024-05-26 08:35:45 +01:00
|
|
|
let Some(repo_config) = self.repo_details.repo_config.clone() else {
|
2024-04-10 15:18:48 +01:00
|
|
|
warn!("No config loaded");
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
2024-04-11 07:15:19 +01:00
|
|
|
let next_ahead_of_main = msg.main != msg.next;
|
|
|
|
let dev_ahead_of_next = msg.next != msg.dev;
|
2024-05-04 12:37:35 +01:00
|
|
|
info!(next_ahead_of_main, dev_ahead_of_next, "StartMonitoring");
|
2024-04-11 07:15:19 +01:00
|
|
|
|
2024-04-09 19:30:05 +01:00
|
|
|
let addr = ctx.address();
|
2024-04-16 22:21:55 +01:00
|
|
|
let forge = self.forge.clone();
|
2024-04-10 15:18:48 +01:00
|
|
|
|
2024-04-09 19:30:05 +01:00
|
|
|
if next_ahead_of_main {
|
2024-05-05 08:17:32 +01:00
|
|
|
status::check_next(msg.next, addr, forge, self.message_token)
|
2024-05-04 12:37:35 +01:00
|
|
|
.in_current_span()
|
2024-04-09 19:30:05 +01:00
|
|
|
.into_actor(self)
|
|
|
|
.wait(ctx);
|
|
|
|
} else if dev_ahead_of_next {
|
2024-05-09 21:53:50 +01:00
|
|
|
if let Some(repository) = self.repository.clone() {
|
|
|
|
branch::advance_next(
|
|
|
|
msg.next,
|
|
|
|
msg.dev_commit_history,
|
2024-05-26 08:35:45 +01:00
|
|
|
self.repo_details.clone(),
|
2024-05-09 21:53:50 +01:00
|
|
|
repo_config,
|
|
|
|
repository,
|
|
|
|
addr,
|
|
|
|
self.message_token,
|
|
|
|
)
|
|
|
|
.in_current_span()
|
|
|
|
.into_actor(self)
|
|
|
|
.wait(ctx);
|
|
|
|
}
|
2024-04-09 19:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Message)]
|
|
|
|
#[rtype(result = "()")]
|
2024-05-23 16:50:36 +01:00
|
|
|
pub struct WebhookRegistered(webhook::WebhookId, webhook::WebhookAuth);
|
2024-04-09 19:30:05 +01:00
|
|
|
impl Handler<WebhookRegistered> for RepoActor {
|
|
|
|
type Result = ();
|
2024-05-26 08:35:45 +01:00
|
|
|
#[tracing::instrument(name = "RepoActor::WebhookRegistered", skip_all, fields(repo = %self.repo_details, webhook_id = %msg.0))]
|
2024-04-09 19:30:05 +01:00
|
|
|
fn handle(&mut self, msg: WebhookRegistered, _ctx: &mut Self::Context) -> Self::Result {
|
|
|
|
self.webhook_id.replace(msg.0);
|
2024-04-14 06:48:26 +01:00
|
|
|
self.webhook_auth.replace(msg.1);
|
2024-04-09 18:18:19 +01:00
|
|
|
}
|
|
|
|
}
|
2024-04-09 22:43:54 +01:00
|
|
|
|
|
|
|
#[derive(Message)]
|
|
|
|
#[rtype(result = "()")]
|
2024-05-15 21:01:19 +01:00
|
|
|
pub struct AdvanceMainTo(git::Commit);
|
2024-04-09 22:43:54 +01:00
|
|
|
impl Handler<AdvanceMainTo> for RepoActor {
|
|
|
|
type Result = ();
|
2024-05-26 08:35:45 +01:00
|
|
|
#[tracing::instrument(name = "RepoActor::AdvanceMainTo", skip_all, fields(repo = %self.repo_details, commit = %msg.0))]
|
2024-04-09 22:43:54 +01:00
|
|
|
fn handle(&mut self, msg: AdvanceMainTo, ctx: &mut Self::Context) -> Self::Result {
|
2024-05-26 08:35:45 +01:00
|
|
|
let Some(repo_config) = self.repo_details.repo_config.clone() else {
|
2024-04-10 17:36:08 +01:00
|
|
|
warn!("No config loaded");
|
|
|
|
return;
|
|
|
|
};
|
2024-05-09 21:53:50 +01:00
|
|
|
let Some(repository) = self.repository.clone() else {
|
|
|
|
warn!("No repository opened");
|
|
|
|
return;
|
|
|
|
};
|
2024-05-26 08:35:45 +01:00
|
|
|
let repo_details = self.repo_details.clone();
|
2024-05-03 17:50:50 +01:00
|
|
|
let addr = ctx.address();
|
2024-05-22 08:41:30 +01:00
|
|
|
let message_token = self.message_token;
|
|
|
|
async move {
|
2024-05-26 08:35:45 +01:00
|
|
|
branch::advance_main(msg.0, &repo_details, &repo_config, &repository).await;
|
2024-05-22 08:41:30 +01:00
|
|
|
match repo_config.source() {
|
2024-05-23 16:50:36 +01:00
|
|
|
git_next_config::RepoConfigSource::Repo => addr.do_send(LoadConfigFromRepo),
|
|
|
|
git_next_config::RepoConfigSource::Server => {
|
|
|
|
addr.do_send(ValidateRepo { message_token })
|
|
|
|
}
|
2024-05-22 08:41:30 +01:00
|
|
|
}
|
|
|
|
}
|
2024-05-09 21:53:50 +01:00
|
|
|
.in_current_span()
|
|
|
|
.into_actor(self)
|
|
|
|
.wait(ctx);
|
2024-04-09 22:43:54 +01:00
|
|
|
}
|
|
|
|
}
|
2024-05-23 08:30:58 +01:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, derive_more::Display)]
|
|
|
|
pub struct MessageToken(u32);
|
|
|
|
impl MessageToken {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self::default()
|
|
|
|
}
|
|
|
|
pub const fn next(&self) -> Self {
|
|
|
|
Self(self.0 + 1)
|
|
|
|
}
|
|
|
|
}
|