diff --git a/src/server/actors/repo/mod.rs b/src/server/actors/repo/mod.rs index 557460f4..3223d7da 100644 --- a/src/server/actors/repo/mod.rs +++ b/src/server/actors/repo/mod.rs @@ -11,14 +11,14 @@ use crate::server::{ actors::repo::webhook::WebhookAuth, config::{RepoConfig, RepoDetails, Webhook}, gitforge, - types::MessageToken, + types::{MessageToken, ServerGeneration}, }; use self::webhook::WebhookId; pub struct RepoActor { + generation: ServerGeneration, message_token: MessageToken, - span: tracing::Span, details: RepoDetails, webhook: Webhook, webhook_id: Option, // INFO: if [None] then no webhook is configured @@ -30,8 +30,12 @@ pub struct RepoActor { forge: gitforge::Forge, } impl RepoActor { - pub(crate) fn new(details: RepoDetails, webhook: Webhook, net: Network) -> Self { - let span = tracing::info_span!("RepoActor", repo = %details); + pub(crate) fn new( + details: RepoDetails, + webhook: Webhook, + generation: ServerGeneration, + net: Network, + ) -> Self { let forge = match details.forge.forge_type { #[cfg(feature = "forgejo")] crate::server::config::ForgeType::ForgeJo => { @@ -42,8 +46,8 @@ impl RepoActor { }; debug!(?forge, "new"); Self { + generation, message_token: MessageToken::new(), - span, details, webhook, webhook_id: None, @@ -58,13 +62,14 @@ impl RepoActor { } impl Actor for RepoActor { type Context = Context; + #[tracing::instrument(name = "RepoActor::stopping", skip_all)] fn stopping(&mut self, ctx: &mut Self::Context) -> Running { - let _gaurd = self.span.enter(); info!("Checking webhook"); match self.webhook_id.take() { Some(webhook_id) => { let repo_details = self.details.clone(); let net = self.net.clone(); + info!(%webhook_id, "Unregistring webhook"); webhook::unregister(webhook_id, repo_details, net) .in_current_span() .into_actor(self) @@ -79,8 +84,8 @@ impl std::fmt::Display for RepoActor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{}/{}", - self.details.forge.forge_name, self.details.repo_alias + "{}:{}:{}", + self.generation, self.details.forge.forge_name, self.details.repo_alias ) } } diff --git a/src/server/actors/server.rs b/src/server/actors/server.rs index 6295eeb0..33612171 100644 --- a/src/server/actors/server.rs +++ b/src/server/actors/server.rs @@ -14,6 +14,7 @@ use crate::server::{ ForgeConfig, ForgeName, GitDir, RepoAlias, RepoDetails, ServerConfig, ServerRepoConfig, ServerStorage, Webhook, }, + types::ServerGeneration, }; #[derive(Debug, derive_more::Display, derive_more::From)] @@ -33,6 +34,7 @@ pub enum Error { type Result = core::result::Result; pub struct Server { + generation: ServerGeneration, fs: FileSystem, net: Network, } @@ -85,8 +87,12 @@ impl Handler for Server { } } impl Server { - pub const fn new(fs: FileSystem, net: Network) -> Self { - Self { fs, net } + pub const fn new(generation: ServerGeneration, fs: FileSystem, net: Network) -> Self { + Self { + generation, + fs, + net, + } } fn create_forge_data_directories( &self, @@ -144,6 +150,7 @@ impl Server { let server_storage = server_storage.clone(); let webhook = webhook.clone(); let net = self.net.clone(); + let generation = self.generation; move |(repo_alias, server_repo_config)| { let span = tracing::info_span!("create_actor", alias = %repo_alias, config = %server_repo_config); let _guard = span.enter(); @@ -162,6 +169,7 @@ impl Server { // INFO: can't canonicalise gitdir as the path needs to exist to do that and we may not // have cloned the repo yet let repo_details = RepoDetails::new( + generation, &repo_alias, server_repo_config, &forge_name, @@ -169,7 +177,7 @@ impl Server { gitdir, ); info!("Starting Repo Actor"); - let actor = RepoActor::new(repo_details, webhook.clone(), net.clone()); + let actor = RepoActor::new(repo_details, webhook.clone(), generation, net.clone()); (forge_name.clone(), repo_alias, actor) } } diff --git a/src/server/config/mod.rs b/src/server/config/mod.rs index ec4c575f..b12ba138 100644 --- a/src/server/config/mod.rs +++ b/src/server/config/mod.rs @@ -14,6 +14,8 @@ use serde::Deserialize; use kxio::fs::FileSystem; use tracing::info; +use crate::server::types::ServerGeneration; + #[derive(Debug, derive_more::From, derive_more::Display)] pub enum Error { Io(std::io::Error), @@ -359,6 +361,7 @@ impl Deref for BranchName { /// The derived information about a repo, used to interact with it #[derive(Clone, Debug)] pub struct RepoDetails { + pub generation: ServerGeneration, pub repo_alias: RepoAlias, pub repo_path: RepoPath, pub branch: BranchName, @@ -368,6 +371,7 @@ pub struct RepoDetails { } impl RepoDetails { pub fn new( + generation: ServerGeneration, repo_alias: &RepoAlias, server_repo_config: &ServerRepoConfig, forge_name: &ForgeName, @@ -375,6 +379,7 @@ impl RepoDetails { gitdir: GitDir, ) -> Self { Self { + generation, repo_alias: repo_alias.clone(), repo_path: RepoPath(server_repo_config.repo.clone()), repo_config: server_repo_config.repo_config(), @@ -414,7 +419,8 @@ impl Display for RepoDetails { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, - "{}:{}/{}:{}@{}/{}@{}", + "gen-{}:{}:{}/{}:{}@{}/{}@{}", + self.generation, self.forge.forge_type, self.forge.forge_name, self.repo_alias, diff --git a/src/server/config/tests.rs b/src/server/config/tests.rs index c90fb7c0..cc83fdba 100644 --- a/src/server/config/tests.rs +++ b/src/server/config/tests.rs @@ -157,6 +157,7 @@ fn repo_details_find_default_push_remote_finds_correct_remote() -> Result<()> { let cwd = std::env::current_dir().map_err(RepoValidationError::Io)?; let mut repo_details = common::repo_details( 1, + ServerGeneration::new(), common::forge_details(1, ForgeType::MockForge), None, GitDir::new(&cwd), // Server GitDir - should be ignored @@ -180,6 +181,7 @@ fn gitdir_validate_should_pass_a_valid_git_repo() -> Result<()> { let cwd = std::env::current_dir().map_err(RepoValidationError::Io)?; let mut repo_details = common::repo_details( 1, + ServerGeneration::new(), common::forge_details(1, ForgeType::MockForge), None, GitDir::new(&cwd), // Server GitDir - should be ignored @@ -198,6 +200,7 @@ fn gitdir_validate_should_fail_a_non_git_dir() { let cwd = fs.base(); let repo_details = common::repo_details( 1, + ServerGeneration::new(), common::forge_details(1, ForgeType::MockForge), None, GitDir::new(cwd), // Server GitDir - should be ignored @@ -211,6 +214,7 @@ fn gitdir_validate_should_fail_a_git_repo_with_wrong_remote() { let_assert!(Ok(cwd) = std::env::current_dir().map_err(RepoValidationError::Io)); let mut repo_details = common::repo_details( 1, + ServerGeneration::new(), common::forge_details(1, ForgeType::MockForge), None, GitDir::new(&cwd), // Server GitDir - should be ignored diff --git a/src/server/gitforge/tests/common.rs b/src/server/gitforge/tests/common.rs index 44b83ea7..be7242a5 100644 --- a/src/server/gitforge/tests/common.rs +++ b/src/server/gitforge/tests/common.rs @@ -1,6 +1,9 @@ -use crate::server::config::{ - ApiToken, BranchName, ForgeDetails, ForgeName, ForgeType, GitDir, Hostname, RepoAlias, - RepoBranches, RepoConfig, RepoDetails, RepoPath, User, +use crate::server::{ + config::{ + ApiToken, BranchName, ForgeDetails, ForgeName, ForgeType, GitDir, Hostname, RepoAlias, + RepoBranches, RepoConfig, RepoDetails, RepoPath, User, + }, + types::ServerGeneration, }; pub fn forge_details(n: u32, forge_type: ForgeType) -> ForgeDetails { @@ -30,11 +33,13 @@ pub fn forge_name(n: u32) -> ForgeName { } pub fn repo_details( n: u32, + generation: ServerGeneration, forge: ForgeDetails, repo_config: Option, gitdir: GitDir, ) -> RepoDetails { RepoDetails { + generation, repo_alias: repo_alias(n), repo_path: repo_path(n), gitdir, diff --git a/src/server/gitforge/tests/forgejo.rs b/src/server/gitforge/tests/forgejo.rs index 4258e975..08157701 100644 --- a/src/server/gitforge/tests/forgejo.rs +++ b/src/server/gitforge/tests/forgejo.rs @@ -2,7 +2,10 @@ use assert2::let_assert; use kxio::network::{MockNetwork, StatusCode}; -use crate::server::config::{BranchName, ForgeType}; +use crate::server::{ + config::{BranchName, ForgeType}, + types::ServerGeneration, +}; use super::*; @@ -14,6 +17,7 @@ fn test_name() { let net = Network::new_mock(); let repo_details = common::repo_details( 1, + ServerGeneration::new(), common::forge_details(1, ForgeType::MockForge), Some(common::repo_config(1)), GitDir::new(fs.base()), @@ -40,6 +44,7 @@ async fn test_branches_get() { let repo_details = common::repo_details( 1, + ServerGeneration::new(), common::forge_details(1, ForgeType::MockForge), Some(common::repo_config(1)), GitDir::new(fs.base()), diff --git a/src/server/mod.rs b/src/server/mod.rs index b38ae8b5..418d211e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -11,7 +11,10 @@ use std::path::PathBuf; use tracing::{error, info, level_filters::LevelFilter}; -use crate::{fs::FileSystem, server::actors::server::Server}; +use crate::{ + fs::FileSystem, + server::{actors::server::Server, types::ServerGeneration}, +}; pub fn init(fs: FileSystem) { let file_name = "git-next-server.toml"; @@ -37,22 +40,26 @@ pub fn init(fs: FileSystem) { pub async fn start(fs: FileSystem, net: Network) { init_logging(); - info!("Starting Server..."); - let server_config = match config::ServerConfig::load(&fs) { - Ok(server_config) => server_config, - Err(err) => { - error!("Failed to load config file. Error: {}", err); - return; - } - }; + let generation = ServerGeneration::new(); + { + let span = tracing::info_span!("Server", %generation); + let _guard = span.enter(); + info!("Starting Server..."); + let server_config = match config::ServerConfig::load(&fs) { + Ok(server_config) => server_config, + Err(err) => { + error!("Failed to load config file. Error: {}", err); + return; + } + }; - let server = Server::new(fs, net).start(); - server.do_send(server_config); + let server = Server::new(generation, fs.clone(), net.clone()).start(); + server.do_send(server_config); + } info!("Server running - Press Ctrl-C to stop..."); let _ = actix_rt::signal::ctrl_c().await; info!("Ctrl-C received, shutting down..."); - drop(server); } pub fn init_logging() { diff --git a/src/server/types.rs b/src/server/types.rs index 58da629b..dd7b0b3a 100644 --- a/src/server/types.rs +++ b/src/server/types.rs @@ -35,3 +35,19 @@ impl std::fmt::Display for MessageToken { write!(f, "{}", self.0) } } + +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct ServerGeneration(u32); +impl ServerGeneration { + pub fn new() -> Self { + Self::default() + } + pub const fn next(&self) -> Self { + Self(self.0 + 1) + } +} +impl std::fmt::Display for ServerGeneration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +}