forked from kemitix/git-next
chore(logging): clean up and reformat logging
This commit is contained in:
parent
5ba5a126c3
commit
7516ec1dc1
18 changed files with 209 additions and 118 deletions
|
@ -1,5 +1,4 @@
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
todo_check:
|
todo_check:
|
||||||
# INFO: https://woodpecker-ci.org/plugins/TODO-Checker
|
# INFO: https://woodpecker-ci.org/plugins/TODO-Checker
|
||||||
image: codeberg.org/epsilon_02/todo-checker:1.1
|
image: codeberg.org/epsilon_02/todo-checker:1.1
|
||||||
|
@ -8,7 +7,7 @@ steps:
|
||||||
branch: next
|
branch: next
|
||||||
settings:
|
settings:
|
||||||
# git-next-woodpecker-todo-checker - read:issue
|
# git-next-woodpecker-todo-checker - read:issue
|
||||||
repository_token: '776a3b928b852472c2af727a360c85c00af64b9f'
|
repository_token: "776a3b928b852472c2af727a360c85c00af64b9f"
|
||||||
prefix_regex: "(#|//) (TODO|FIXME): "
|
prefix_regex: "(#|//) (TODO|FIXME): "
|
||||||
debug: false
|
debug: false
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ steps:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
commands:
|
commands:
|
||||||
- cargo fmt --all -- --check
|
- cargo fmt --all -- --check
|
||||||
- cargo clippy -- -D warnings -W clippy::nursery -W clippy::unwrap_used -W clippy::expect_used
|
- cargo clippy -- -D warnings
|
||||||
- cargo build
|
- cargo build
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
|
|
@ -75,7 +75,7 @@ anyhow = "1.0"
|
||||||
cc-cli = { version = "0.1" }
|
cc-cli = { version = "0.1" }
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
nursery = "warn"
|
nursery = { level = "warn", priority = -1 }
|
||||||
# pedantic = "warn"
|
# pedantic = "warn"
|
||||||
unwrap_used = "warn"
|
unwrap_used = "warn"
|
||||||
expect_used = "warn"
|
expect_used = "warn"
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use tracing::error;
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::server::{config::RepoDetails, gitforge};
|
use crate::server::{config, gitforge};
|
||||||
|
|
||||||
use super::{LoadedConfig, RepoActor};
|
use super::{LoadedConfig, RepoActor};
|
||||||
|
|
||||||
/// Loads the [RepoConfig] from the `.git-next.toml` file in the repository
|
/// Loads the [RepoConfig] from the `.git-next.toml` file in the repository
|
||||||
pub async fn load(repo_details: RepoDetails, addr: Addr<RepoActor>, forge: gitforge::Forge) {
|
#[tracing::instrument(skip_all, fields(branch = %repo_details.branch))]
|
||||||
let repo_config = match crate::server::config::load::load(&repo_details, &forge).await {
|
pub async fn load(
|
||||||
|
repo_details: config::RepoDetails,
|
||||||
|
addr: Addr<RepoActor>,
|
||||||
|
forge: gitforge::Forge,
|
||||||
|
) {
|
||||||
|
info!("Loading .git-next.toml from repo");
|
||||||
|
let repo_config = match config::load::load(&repo_details, &forge).await {
|
||||||
Ok(repo_config) => repo_config,
|
Ok(repo_config) => repo_config,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(?err, "Failed to load config");
|
error!(?err, "Failed to load config");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
info!("Loaded .git-next.toml from repo");
|
||||||
addr.do_send(LoadedConfig(repo_config));
|
addr.do_send(LoadedConfig(repo_config));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub mod webhook;
|
||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use kxio::network::Network;
|
use kxio::network::Network;
|
||||||
use tracing::{info, warn};
|
use tracing::{debug, info, warn, Instrument};
|
||||||
|
|
||||||
use crate::server::{
|
use crate::server::{
|
||||||
actors::repo::webhook::WebhookAuth,
|
actors::repo::webhook::WebhookAuth,
|
||||||
|
@ -16,6 +16,7 @@ use crate::server::{
|
||||||
use self::webhook::WebhookId;
|
use self::webhook::WebhookId;
|
||||||
|
|
||||||
pub struct RepoActor {
|
pub struct RepoActor {
|
||||||
|
span: tracing::Span,
|
||||||
details: RepoDetails,
|
details: RepoDetails,
|
||||||
webhook: Webhook,
|
webhook: Webhook,
|
||||||
webhook_id: Option<WebhookId>, // INFO: if [None] then no webhook is configured
|
webhook_id: Option<WebhookId>, // INFO: if [None] then no webhook is configured
|
||||||
|
@ -28,6 +29,7 @@ pub struct RepoActor {
|
||||||
}
|
}
|
||||||
impl RepoActor {
|
impl RepoActor {
|
||||||
pub(crate) fn new(details: RepoDetails, webhook: Webhook, net: Network) -> Self {
|
pub(crate) fn new(details: RepoDetails, webhook: Webhook, net: Network) -> Self {
|
||||||
|
let span = tracing::info_span!("RepoActor", repo = %details);
|
||||||
let forge = match details.forge.forge_type {
|
let forge = match details.forge.forge_type {
|
||||||
#[cfg(feature = "forgejo")]
|
#[cfg(feature = "forgejo")]
|
||||||
crate::server::config::ForgeType::ForgeJo => {
|
crate::server::config::ForgeType::ForgeJo => {
|
||||||
|
@ -36,7 +38,9 @@ impl RepoActor {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
crate::server::config::ForgeType::MockForge => gitforge::Forge::new_mock(),
|
crate::server::config::ForgeType::MockForge => gitforge::Forge::new_mock(),
|
||||||
};
|
};
|
||||||
|
debug!(?forge, "new");
|
||||||
Self {
|
Self {
|
||||||
|
span,
|
||||||
details,
|
details,
|
||||||
webhook,
|
webhook,
|
||||||
webhook_id: None,
|
webhook_id: None,
|
||||||
|
@ -52,11 +56,14 @@ impl RepoActor {
|
||||||
impl Actor for RepoActor {
|
impl Actor for RepoActor {
|
||||||
type Context = Context<Self>;
|
type Context = Context<Self>;
|
||||||
fn stopping(&mut self, ctx: &mut Self::Context) -> Running {
|
fn stopping(&mut self, ctx: &mut Self::Context) -> Running {
|
||||||
|
let _gaurd = self.span.enter();
|
||||||
|
info!("Checking webhook");
|
||||||
match self.webhook_id.take() {
|
match self.webhook_id.take() {
|
||||||
Some(webhook_id) => {
|
Some(webhook_id) => {
|
||||||
let repo_details = self.details.clone();
|
let repo_details = self.details.clone();
|
||||||
let net = self.net.clone();
|
let net = self.net.clone();
|
||||||
webhook::unregister(webhook_id, repo_details, net)
|
webhook::unregister(webhook_id, repo_details, net)
|
||||||
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
Running::Continue
|
Running::Continue
|
||||||
|
@ -69,7 +76,7 @@ impl std::fmt::Display for RepoActor {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"RepoActor: {}/{}",
|
"{}/{}",
|
||||||
self.details.forge.forge_name, self.details.repo_alias
|
self.details.forge.forge_name, self.details.repo_alias
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -80,9 +87,9 @@ impl std::fmt::Display for RepoActor {
|
||||||
pub struct CloneRepo;
|
pub struct CloneRepo;
|
||||||
impl Handler<CloneRepo> for RepoActor {
|
impl Handler<CloneRepo> for RepoActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
#[tracing::instrument(skip_all, fields(%self))]
|
#[tracing::instrument(name = "RepoActor::CloneRepo", skip_all, fields(repo = %self, gitdir = %self.details.gitdir))]
|
||||||
fn handle(&mut self, _msg: CloneRepo, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _msg: CloneRepo, ctx: &mut Self::Context) -> Self::Result {
|
||||||
info!(%self.details, "Clone/Update Repo");
|
info!("Message Received");
|
||||||
let gitdir = self.details.gitdir.clone();
|
let gitdir = self.details.gitdir.clone();
|
||||||
match self.forge.repo_clone(gitdir) {
|
match self.forge.repo_clone(gitdir) {
|
||||||
Ok(_) => ctx.address().do_send(LoadConfigFromRepo),
|
Ok(_) => ctx.address().do_send(LoadConfigFromRepo),
|
||||||
|
@ -96,13 +103,14 @@ impl Handler<CloneRepo> for RepoActor {
|
||||||
pub struct LoadConfigFromRepo;
|
pub struct LoadConfigFromRepo;
|
||||||
impl Handler<LoadConfigFromRepo> for RepoActor {
|
impl Handler<LoadConfigFromRepo> for RepoActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
#[tracing::instrument(skip_all, fields(%self))]
|
#[tracing::instrument(name = "RepoActor::LoadConfigFromRepo", skip_all, fields(repo = %self))]
|
||||||
fn handle(&mut self, _msg: LoadConfigFromRepo, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _msg: LoadConfigFromRepo, ctx: &mut Self::Context) -> Self::Result {
|
||||||
info!(%self.details, "Loading .git-next.toml from repo");
|
info!("Message Received");
|
||||||
let details = self.details.clone();
|
let details = self.details.clone();
|
||||||
let addr = ctx.address();
|
let addr = ctx.address();
|
||||||
let forge = self.forge.clone();
|
let forge = self.forge.clone();
|
||||||
config::load(details, addr, forge)
|
config::load(details, addr, forge)
|
||||||
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
|
@ -113,10 +121,10 @@ impl Handler<LoadConfigFromRepo> for RepoActor {
|
||||||
struct LoadedConfig(pub RepoConfig);
|
struct LoadedConfig(pub RepoConfig);
|
||||||
impl Handler<LoadedConfig> for RepoActor {
|
impl Handler<LoadedConfig> for RepoActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
#[tracing::instrument(skip_all, fields(%self))]
|
#[tracing::instrument(name = "RepoActor::LoadedConfig", skip_all, fields(repo = %self.details, branches = %msg.0))]
|
||||||
fn handle(&mut self, msg: LoadedConfig, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: LoadedConfig, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
info!("Message Received");
|
||||||
let repo_config = msg.0;
|
let repo_config = msg.0;
|
||||||
info!(%self.details, %repo_config, "Config loaded");
|
|
||||||
self.details.repo_config.replace(repo_config);
|
self.details.repo_config.replace(repo_config);
|
||||||
if self.webhook_id.is_none() {
|
if self.webhook_id.is_none() {
|
||||||
webhook::register(
|
webhook::register(
|
||||||
|
@ -125,6 +133,7 @@ impl Handler<LoadedConfig> for RepoActor {
|
||||||
ctx.address(),
|
ctx.address(),
|
||||||
self.net.clone(),
|
self.net.clone(),
|
||||||
)
|
)
|
||||||
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
|
@ -137,13 +146,14 @@ impl Handler<LoadedConfig> for RepoActor {
|
||||||
pub struct ValidateRepo;
|
pub struct ValidateRepo;
|
||||||
impl Handler<ValidateRepo> for RepoActor {
|
impl Handler<ValidateRepo> for RepoActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
#[tracing::instrument(skip_all, fields(%self))]
|
#[tracing::instrument(name = "RepoActor::ValidateRepo", skip_all, fields(repo = %self.details))]
|
||||||
fn handle(&mut self, _msg: ValidateRepo, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _msg: ValidateRepo, ctx: &mut Self::Context) -> Self::Result {
|
||||||
info!("ValidateRepo");
|
info!("Message Received");
|
||||||
if let Some(repo_config) = self.details.repo_config.clone() {
|
if let Some(repo_config) = self.details.repo_config.clone() {
|
||||||
let forge = self.forge.clone();
|
let forge = self.forge.clone();
|
||||||
let addr = ctx.address();
|
let addr = ctx.address();
|
||||||
async move { forge.branches_validate_positions(repo_config, addr).await }
|
async move { forge.branches_validate_positions(repo_config, addr).await }
|
||||||
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
|
@ -160,9 +170,9 @@ pub struct StartMonitoring {
|
||||||
}
|
}
|
||||||
impl Handler<StartMonitoring> for RepoActor {
|
impl Handler<StartMonitoring> for RepoActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
#[tracing::instrument(skip_all, fields(%self))]
|
#[tracing::instrument(name = "RepoActor::StartMonitoring", skip_all, fields(repo = %self.details, main = %msg.main, next= %msg.next, dev = %msg.dev))]
|
||||||
fn handle(&mut self, msg: StartMonitoring, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: StartMonitoring, ctx: &mut Self::Context) -> Self::Result {
|
||||||
info!("StartMonitoring");
|
info!("Message Received");
|
||||||
let Some(repo_config) = self.details.repo_config.clone() else {
|
let Some(repo_config) = self.details.repo_config.clone() else {
|
||||||
warn!("No config loaded");
|
warn!("No config loaded");
|
||||||
return;
|
return;
|
||||||
|
@ -170,7 +180,7 @@ impl Handler<StartMonitoring> for RepoActor {
|
||||||
|
|
||||||
let next_ahead_of_main = msg.main != msg.next;
|
let next_ahead_of_main = msg.main != msg.next;
|
||||||
let dev_ahead_of_next = msg.next != msg.dev;
|
let dev_ahead_of_next = msg.next != msg.dev;
|
||||||
info!(%msg.main, %msg.next, %msg.dev, next_ahead_of_main, dev_ahead_of_next, "StartMonitoring");
|
info!(next_ahead_of_main, dev_ahead_of_next, "StartMonitoring");
|
||||||
|
|
||||||
let repo_details = self.details.clone();
|
let repo_details = self.details.clone();
|
||||||
let webhook = self.webhook.clone();
|
let webhook = self.webhook.clone();
|
||||||
|
@ -180,14 +190,17 @@ impl Handler<StartMonitoring> for RepoActor {
|
||||||
|
|
||||||
if next_ahead_of_main {
|
if next_ahead_of_main {
|
||||||
status::check_next(msg.next, addr, forge)
|
status::check_next(msg.next, addr, forge)
|
||||||
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
} else if dev_ahead_of_next {
|
} else if dev_ahead_of_next {
|
||||||
branch::advance_next(msg.next, msg.dev_commit_history, repo_config, forge, addr)
|
branch::advance_next(msg.next, msg.dev_commit_history, repo_config, forge, addr)
|
||||||
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
} else if self.webhook_id.is_none() {
|
} else if self.webhook_id.is_none() {
|
||||||
webhook::register(repo_details, webhook, addr, net)
|
webhook::register(repo_details, webhook, addr, net)
|
||||||
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
|
@ -199,8 +212,9 @@ impl Handler<StartMonitoring> for RepoActor {
|
||||||
pub struct WebhookRegistered(pub WebhookId, pub WebhookAuth);
|
pub struct WebhookRegistered(pub WebhookId, pub WebhookAuth);
|
||||||
impl Handler<WebhookRegistered> for RepoActor {
|
impl Handler<WebhookRegistered> for RepoActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
#[tracing::instrument(skip_all, fields(%self))]
|
#[tracing::instrument(name = "RepoActor::WebhookRegistered", skip_all, fields(webhook_id = %msg.0))]
|
||||||
fn handle(&mut self, msg: WebhookRegistered, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: WebhookRegistered, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
info!("Message Received");
|
||||||
self.webhook_id.replace(msg.0);
|
self.webhook_id.replace(msg.0);
|
||||||
self.webhook_auth.replace(msg.1);
|
self.webhook_auth.replace(msg.1);
|
||||||
}
|
}
|
||||||
|
@ -211,8 +225,9 @@ impl Handler<WebhookRegistered> for RepoActor {
|
||||||
pub struct AdvanceMainTo(pub gitforge::Commit);
|
pub struct AdvanceMainTo(pub gitforge::Commit);
|
||||||
impl Handler<AdvanceMainTo> for RepoActor {
|
impl Handler<AdvanceMainTo> for RepoActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
#[tracing::instrument(skip_all, fields(%self))]
|
#[tracing::instrument(name = "RepoActor::AdvanceMainTo", skip_all, fields(commit = %msg.0))]
|
||||||
fn handle(&mut self, msg: AdvanceMainTo, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: AdvanceMainTo, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
info!("Message Received");
|
||||||
let Some(repo_config) = self.details.repo_config.clone() else {
|
let Some(repo_config) = self.details.repo_config.clone() else {
|
||||||
warn!("No config loaded");
|
warn!("No config loaded");
|
||||||
return;
|
return;
|
||||||
|
@ -220,6 +235,7 @@ impl Handler<AdvanceMainTo> for RepoActor {
|
||||||
let forge = self.forge.clone();
|
let forge = self.forge.clone();
|
||||||
let addr = ctx.address();
|
let addr = ctx.address();
|
||||||
branch::advance_main(msg.0, repo_config, forge, addr)
|
branch::advance_main(msg.0, repo_config, forge, addr)
|
||||||
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,12 +57,12 @@ impl Deref for WebhookAuth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(%webhook_id))]
|
||||||
pub async fn unregister(
|
pub async fn unregister(
|
||||||
webhook_id: WebhookId,
|
webhook_id: WebhookId,
|
||||||
repo_details: crate::server::config::RepoDetails,
|
repo_details: crate::server::config::RepoDetails,
|
||||||
net: network::Network,
|
net: network::Network,
|
||||||
) {
|
) {
|
||||||
info!(?webhook_id, "unregister webhook");
|
|
||||||
let hostname = &repo_details.forge.hostname;
|
let hostname = &repo_details.forge.hostname;
|
||||||
let repo_path = repo_details.repo_path;
|
let repo_path = repo_details.repo_path;
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
|
@ -81,11 +81,12 @@ pub async fn unregister(
|
||||||
);
|
);
|
||||||
let result = net.delete(request).await;
|
let result = net.delete(request).await;
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => info!(?webhook_id, "unregistered webhook"),
|
Ok(_) => info!("unregistered webhook"),
|
||||||
Err(err) => warn!(?webhook_id, ?err, "Failed to unregister webhook"),
|
Err(err) => warn!(?err, "Failed to unregister webhook"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn register(
|
pub async fn register(
|
||||||
repo_details: crate::server::config::RepoDetails,
|
repo_details: crate::server::config::RepoDetails,
|
||||||
webhook: Webhook,
|
webhook: Webhook,
|
||||||
|
@ -103,7 +104,6 @@ pub async fn register(
|
||||||
unregister(webhook_id, repo_details.clone(), net.clone()).await;
|
unregister(webhook_id, repo_details.clone(), net.clone()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Registering webhook");
|
|
||||||
let hostname = &repo_details.forge.hostname;
|
let hostname = &repo_details.forge.hostname;
|
||||||
let repo_path = repo_details.repo_path;
|
let repo_path = repo_details.repo_path;
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
|
@ -138,7 +138,7 @@ pub async fn register(
|
||||||
match result {
|
match result {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
if let Some(hook) = response.response_body() {
|
if let Some(hook) = response.response_body() {
|
||||||
info!("Webhook registered");
|
info!(webhook_id = %hook.id, "Webhook registered");
|
||||||
addr.do_send(WebhookRegistered(hook.id(), authorisation));
|
addr.do_send(WebhookRegistered(hook.id(), authorisation));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,19 @@ use actix::prelude::*;
|
||||||
pub use message::WebhookMessage;
|
pub use message::WebhookMessage;
|
||||||
pub use router::AddWebhookRecipient;
|
pub use router::AddWebhookRecipient;
|
||||||
pub use router::WebhookRouter;
|
pub use router::WebhookRouter;
|
||||||
|
use tracing::Instrument;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WebhookActor {
|
pub struct WebhookActor {
|
||||||
|
span: tracing::Span,
|
||||||
spawn_handle: Option<actix::SpawnHandle>,
|
spawn_handle: Option<actix::SpawnHandle>,
|
||||||
message_receiver: Recipient<WebhookMessage>,
|
message_receiver: Recipient<WebhookMessage>,
|
||||||
}
|
}
|
||||||
impl WebhookActor {
|
impl WebhookActor {
|
||||||
pub const fn new(message_receiver: Recipient<WebhookMessage>) -> Self {
|
pub fn new(message_receiver: Recipient<WebhookMessage>) -> Self {
|
||||||
|
let span = tracing::info_span!("WebhookActor");
|
||||||
Self {
|
Self {
|
||||||
|
span,
|
||||||
message_receiver,
|
message_receiver,
|
||||||
spawn_handle: None,
|
spawn_handle: None,
|
||||||
}
|
}
|
||||||
|
@ -26,9 +30,10 @@ impl WebhookActor {
|
||||||
impl Actor for WebhookActor {
|
impl Actor for WebhookActor {
|
||||||
type Context = actix::Context<Self>;
|
type Context = actix::Context<Self>;
|
||||||
fn started(&mut self, ctx: &mut Self::Context) {
|
fn started(&mut self, ctx: &mut Self::Context) {
|
||||||
|
let _gaurd = self.span.enter();
|
||||||
let address: Recipient<WebhookMessage> = self.message_receiver.clone();
|
let address: Recipient<WebhookMessage> = self.message_receiver.clone();
|
||||||
let server = server::start(address);
|
let server = server::start(address);
|
||||||
let spawn_handle = ctx.spawn(server.into_actor(self));
|
let spawn_handle = ctx.spawn(server.in_current_span().into_actor(self));
|
||||||
self.spawn_handle.replace(spawn_handle);
|
self.spawn_handle.replace(spawn_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use tracing::debug;
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::server::{actors::webhook::message::WebhookMessage, config::RepoAlias};
|
use crate::server::{actors::webhook::message::WebhookMessage, config::RepoAlias};
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct WebhookRouter {
|
pub struct WebhookRouter {
|
||||||
|
span: tracing::Span,
|
||||||
repos: HashMap<RepoAlias, Recipient<WebhookMessage>>,
|
repos: HashMap<RepoAlias, Recipient<WebhookMessage>>,
|
||||||
}
|
}
|
||||||
impl WebhookRouter {
|
impl WebhookRouter {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
let span = tracing::info_span!("WebhookRouter");
|
||||||
|
Self {
|
||||||
|
span,
|
||||||
|
repos: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Actor for WebhookRouter {
|
impl Actor for WebhookRouter {
|
||||||
|
@ -22,10 +26,11 @@ impl Handler<WebhookMessage> for WebhookRouter {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: WebhookMessage, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: WebhookMessage, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
let _gaurd = self.span.enter();
|
||||||
let repo_alias = RepoAlias(msg.path().clone());
|
let repo_alias = RepoAlias(msg.path().clone());
|
||||||
debug!(?repo_alias, "Router...");
|
debug!(repo = %repo_alias, "Router...");
|
||||||
if let Some(recipient) = self.repos.get(&repo_alias) {
|
if let Some(recipient) = self.repos.get(&repo_alias) {
|
||||||
debug!("Sending to recipient");
|
info!("Sending to Recipient");
|
||||||
recipient.do_send(msg);
|
recipient.do_send(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +43,8 @@ impl Handler<AddWebhookRecipient> for WebhookRouter {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: AddWebhookRecipient, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: AddWebhookRecipient, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
let _gaurd = self.span.enter();
|
||||||
|
info!(repo = %msg.0, "Register Recipient");
|
||||||
self.repos.insert(msg.0, msg.1);
|
self.repos.insert(msg.0, msg.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use kxio::network;
|
|
||||||
use terrors::OneOf;
|
use terrors::OneOf;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
|
@ -7,17 +6,6 @@ use crate::server::{
|
||||||
gitforge::{self, ForgeFileError},
|
gitforge::{self, ForgeFileError},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigFileNotFound;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigIsNotFile;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigDecodeError;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigParseError;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigUnknownError(pub network::StatusCode);
|
|
||||||
|
|
||||||
pub async fn load(
|
pub async fn load(
|
||||||
details: &RepoDetails,
|
details: &RepoDetails,
|
||||||
forge: &gitforge::Forge,
|
forge: &gitforge::Forge,
|
||||||
|
|
|
@ -30,8 +30,11 @@ pub struct ServerConfig {
|
||||||
forge: HashMap<String, ForgeConfig>,
|
forge: HashMap<String, ForgeConfig>,
|
||||||
}
|
}
|
||||||
impl ServerConfig {
|
impl ServerConfig {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub(crate) fn load(fs: &FileSystem) -> Result<Self> {
|
pub(crate) fn load(fs: &FileSystem) -> Result<Self> {
|
||||||
let str = fs.file_read_to_string(&fs.base().join("git-next-server.toml"))?;
|
let file = fs.base().join("git-next-server.toml");
|
||||||
|
info!(?file, "");
|
||||||
|
let str = fs.file_read_to_string(&file)?;
|
||||||
toml::from_str(&str).map_err(Into::into)
|
toml::from_str(&str).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +107,7 @@ impl RepoConfig {
|
||||||
}
|
}
|
||||||
impl Display for RepoConfig {
|
impl Display for RepoConfig {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{:?}", self.branches)
|
write!(f, "{}", self.branches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +141,7 @@ impl RepoBranches {
|
||||||
}
|
}
|
||||||
impl Display for RepoBranches {
|
impl Display for RepoBranches {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{:?}", self)
|
write!(f, "{},{},{}", self.main, self.next, self.dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +183,13 @@ impl ForgeConfig {
|
||||||
}
|
}
|
||||||
impl Display for ForgeConfig {
|
impl Display for ForgeConfig {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{} - {}@{}", self.forge_type, self.user, self.hostname)
|
write!(
|
||||||
|
f,
|
||||||
|
"{}:{}@{}",
|
||||||
|
self.forge_type.to_string().to_lowercase(),
|
||||||
|
self.user,
|
||||||
|
self.hostname
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +240,7 @@ impl AsRef<Self> for ServerRepoConfig {
|
||||||
}
|
}
|
||||||
impl Display for ServerRepoConfig {
|
impl Display for ServerRepoConfig {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{} - {}", self.repo, self.branch)
|
write!(f, "{}@{}", self.repo, self.branch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,12 +411,12 @@ impl Display for RepoDetails {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}/{} ({}): {}:{}/{} @ {}",
|
"{}:{}/{}:{}@{}/{}@{}",
|
||||||
|
self.forge.forge_type,
|
||||||
self.forge.forge_name,
|
self.forge.forge_name,
|
||||||
self.repo_alias,
|
self.repo_alias,
|
||||||
self.forge.forge_type,
|
|
||||||
self.forge.hostname,
|
|
||||||
self.forge.user,
|
self.forge.user,
|
||||||
|
self.forge.hostname,
|
||||||
self.repo_path,
|
self.repo_path,
|
||||||
self.branch,
|
self.branch,
|
||||||
)
|
)
|
||||||
|
@ -468,7 +477,7 @@ pub enum ForgeType {
|
||||||
}
|
}
|
||||||
impl Display for ForgeType {
|
impl Display for ForgeType {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{:?}", self)
|
write!(f, "{}", format!("{:?}", self).to_lowercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,12 +494,13 @@ impl GitDir {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn validate(&self, repo_details: &RepoDetails) -> ValidationResult<()> {
|
pub fn validate(&self, repo_details: &RepoDetails) -> ValidationResult<()> {
|
||||||
let git_remote = repo_details.git_remote();
|
let git_remote = repo_details.git_remote();
|
||||||
use gix::remote::Direction;
|
use gix::remote::Direction;
|
||||||
let push_remote = self.find_default_remote(Direction::Push)?;
|
let push_remote = self.find_default_remote(Direction::Push)?;
|
||||||
let fetch_remote = self.find_default_remote(Direction::Fetch)?;
|
let fetch_remote = self.find_default_remote(Direction::Fetch)?;
|
||||||
info!(gitdir = %self, ?git_remote, ?push_remote, ?fetch_remote, "Gitdir::validate");
|
info!(config = %git_remote, push = %push_remote, fetch = %fetch_remote, "Check remotes match");
|
||||||
if git_remote != push_remote {
|
if git_remote != push_remote {
|
||||||
return Err(RepoValidationError::MismatchDefaultPushRemote {
|
return Err(RepoValidationError::MismatchDefaultPushRemote {
|
||||||
found: push_remote,
|
found: push_remote,
|
||||||
|
@ -506,7 +516,7 @@ impl GitDir {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument(skip_all, fields(?direction))]
|
||||||
fn find_default_remote(
|
fn find_default_remote(
|
||||||
&self,
|
&self,
|
||||||
direction: gix::remote::Direction,
|
direction: gix::remote::Direction,
|
||||||
|
@ -514,7 +524,6 @@ impl GitDir {
|
||||||
let repository = gix::ThreadSafeRepository::open(self.deref())
|
let repository = gix::ThreadSafeRepository::open(self.deref())
|
||||||
.map_err(|e| RepoValidationError::UnableToOpenRepo(e.to_string()))?
|
.map_err(|e| RepoValidationError::UnableToOpenRepo(e.to_string()))?
|
||||||
.to_thread_local();
|
.to_thread_local();
|
||||||
info!(?repository, from = ?self.deref(), "gix::discover");
|
|
||||||
let Some(Ok(remote)) = repository.find_default_remote(gix::remote::Direction::Push) else {
|
let Some(Ok(remote)) = repository.find_default_remote(gix::remote::Direction::Push) else {
|
||||||
return Err(RepoValidationError::NoDefaultPushRemote);
|
return Err(RepoValidationError::NoDefaultPushRemote);
|
||||||
};
|
};
|
||||||
|
@ -548,13 +557,11 @@ impl std::fmt::Display for GitDir {
|
||||||
}
|
}
|
||||||
impl From<&str> for GitDir {
|
impl From<&str> for GitDir {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
info!("GitDir::from::<&str>({value:?})");
|
|
||||||
Self(value.into())
|
Self(value.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<PathBuf> for GitDir {
|
impl From<PathBuf> for GitDir {
|
||||||
fn from(value: PathBuf) -> Self {
|
fn from(value: PathBuf) -> Self {
|
||||||
info!("GitDir::from::<PathBuf>({value:?})");
|
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use kxio::network::{self, Network};
|
use kxio::network::{self, Network};
|
||||||
use tracing::{error, info};
|
use tracing::error;
|
||||||
|
|
||||||
use crate::server::{
|
use crate::server::{
|
||||||
config::{BranchName, RepoDetails},
|
config::{BranchName, RepoDetails},
|
||||||
|
@ -18,7 +18,7 @@ pub async fn get_all(
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/branches?token={token}"
|
"https://{hostname}/api/v1/repos/{repo_path}/branches?token={token}"
|
||||||
));
|
));
|
||||||
|
|
||||||
info!(%url, "Listing branches");
|
// info!(%url, "Listing branches");
|
||||||
let request = network::NetRequest::new(
|
let request = network::NetRequest::new(
|
||||||
network::RequestMethod::Get,
|
network::RequestMethod::Get,
|
||||||
url,
|
url,
|
||||||
|
|
|
@ -7,3 +7,4 @@ pub use fetch::fetch;
|
||||||
pub use get_all::get_all;
|
pub use get_all::get_all;
|
||||||
pub use reset::reset;
|
pub use reset::reset;
|
||||||
pub use validate_positions::validate_positions;
|
pub use validate_positions::validate_positions;
|
||||||
|
pub use validate_positions::ValidatedPositions;
|
||||||
|
|
|
@ -1,20 +1,37 @@
|
||||||
use actix::prelude::*;
|
|
||||||
|
|
||||||
use kxio::network;
|
use kxio::network;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
use crate::server::{
|
use crate::server::{
|
||||||
self,
|
self,
|
||||||
actors::repo::{RepoActor, StartMonitoring},
|
|
||||||
config::{BranchName, RepoConfig, RepoDetails},
|
config::{BranchName, RepoConfig, RepoDetails},
|
||||||
gitforge::{self, ForgeLike},
|
gitforge::{self, ForgeLike},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, derive_more::Display)]
|
||||||
|
pub enum Error {
|
||||||
|
Network(Box<kxio::network::NetworkError>),
|
||||||
|
#[display("Failed to Reset Branch {branch} to {commit}")]
|
||||||
|
FailedToResetBranch {
|
||||||
|
branch: BranchName,
|
||||||
|
commit: gitforge::Commit,
|
||||||
|
},
|
||||||
|
BranchReset(BranchName),
|
||||||
|
BranchHasNoCommits(BranchName),
|
||||||
|
DevBranchNotBasedOn(BranchName),
|
||||||
|
}
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
pub struct ValidatedPositions {
|
||||||
|
pub main: gitforge::Commit,
|
||||||
|
pub next: gitforge::Commit,
|
||||||
|
pub dev: gitforge::Commit,
|
||||||
|
pub dev_commit_history: Vec<gitforge::Commit>,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn validate_positions(
|
pub async fn validate_positions(
|
||||||
forge: &gitforge::forgejo::ForgeJoEnv,
|
forge: &gitforge::forgejo::ForgeJoEnv,
|
||||||
repo_config: RepoConfig,
|
repo_config: RepoConfig,
|
||||||
addr: Addr<RepoActor>,
|
) -> Result<ValidatedPositions, Error> {
|
||||||
) {
|
|
||||||
let repo_details = &forge.repo_details;
|
let repo_details = &forge.repo_details;
|
||||||
// Collect Commit Histories for `main`, `next` and `dev` branches
|
// Collect Commit Histories for `main`, `next` and `dev` branches
|
||||||
let commit_histories = get_commit_histories(repo_details, &repo_config, &forge.net).await;
|
let commit_histories = get_commit_histories(repo_details, &repo_config, &forge.net).await;
|
||||||
|
@ -22,7 +39,7 @@ pub async fn validate_positions(
|
||||||
Ok(commit_histories) => commit_histories,
|
Ok(commit_histories) => commit_histories,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(?err, "Failed to get commit histories");
|
error!(?err, "Failed to get commit histories");
|
||||||
return;
|
return Err(Error::Network(Box::new(err)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,7 +49,7 @@ pub async fn validate_positions(
|
||||||
"No commits on main branch '{}'",
|
"No commits on main branch '{}'",
|
||||||
repo_config.branches().main()
|
repo_config.branches().main()
|
||||||
);
|
);
|
||||||
return;
|
return Err(Error::BranchHasNoCommits(repo_config.branches().main()));
|
||||||
};
|
};
|
||||||
// verify that next is an ancestor of dev, and force update to it main if it isn't
|
// verify that next is an ancestor of dev, and force update to it main if it isn't
|
||||||
let Some(next) = commit_histories.next.first().cloned() else {
|
let Some(next) = commit_histories.next.first().cloned() else {
|
||||||
|
@ -40,7 +57,7 @@ pub async fn validate_positions(
|
||||||
"No commits on next branch '{}",
|
"No commits on next branch '{}",
|
||||||
repo_config.branches().next()
|
repo_config.branches().next()
|
||||||
);
|
);
|
||||||
return;
|
return Err(Error::BranchHasNoCommits(repo_config.branches().next()));
|
||||||
};
|
};
|
||||||
let next_is_ancestor_of_dev = commit_histories.dev.iter().any(|dev| dev == &next);
|
let next_is_ancestor_of_dev = commit_histories.dev.iter().any(|dev| dev == &next);
|
||||||
if !next_is_ancestor_of_dev {
|
if !next_is_ancestor_of_dev {
|
||||||
|
@ -48,11 +65,15 @@ pub async fn validate_positions(
|
||||||
if let Err(err) = forge.branch_reset(
|
if let Err(err) = forge.branch_reset(
|
||||||
repo_config.branches().next(),
|
repo_config.branches().next(),
|
||||||
main.into(),
|
main.into(),
|
||||||
gitforge::Force::From(next.into()),
|
gitforge::Force::From(next.clone().into()),
|
||||||
) {
|
) {
|
||||||
warn!(?err, "Failed to reset next to main");
|
warn!(?err, "Failed to reset next to main");
|
||||||
|
return Err(Error::FailedToResetBranch {
|
||||||
|
branch: repo_config.branches().next(),
|
||||||
|
commit: next,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return Err(Error::BranchReset(repo_config.branches().next()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let next_commits = commit_histories
|
let next_commits = commit_histories
|
||||||
|
@ -69,18 +90,22 @@ pub async fn validate_positions(
|
||||||
if let Err(err) = forge.branch_reset(
|
if let Err(err) = forge.branch_reset(
|
||||||
repo_config.branches().next(),
|
repo_config.branches().next(),
|
||||||
main.into(),
|
main.into(),
|
||||||
gitforge::Force::From(next.into()),
|
gitforge::Force::From(next.clone().into()),
|
||||||
) {
|
) {
|
||||||
warn!(?err, "Failed to reset next to main");
|
warn!(?err, "Failed to reset next to main");
|
||||||
|
return Err(Error::FailedToResetBranch {
|
||||||
|
branch: repo_config.branches().next(),
|
||||||
|
commit: next,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return Err(Error::BranchReset(repo_config.branches().next()));
|
||||||
}
|
}
|
||||||
let Some(next) = next_commits.first().cloned() else {
|
let Some(next) = next_commits.first().cloned() else {
|
||||||
warn!(
|
warn!(
|
||||||
"No commits on next branch '{}'",
|
"No commits on next branch '{}'",
|
||||||
repo_config.branches().next()
|
repo_config.branches().next()
|
||||||
);
|
);
|
||||||
return;
|
return Err(Error::BranchHasNoCommits(repo_config.branches().next()));
|
||||||
};
|
};
|
||||||
let dev_has_next = commit_histories
|
let dev_has_next = commit_histories
|
||||||
.dev
|
.dev
|
||||||
|
@ -92,7 +117,7 @@ pub async fn validate_positions(
|
||||||
repo_config.branches().dev(),
|
repo_config.branches().dev(),
|
||||||
repo_config.branches().next()
|
repo_config.branches().next()
|
||||||
);
|
);
|
||||||
return; // dev is not based on next
|
return Err(Error::DevBranchNotBasedOn(repo_config.branches().next())); // dev is not based on next
|
||||||
}
|
}
|
||||||
let dev_has_main = commit_histories.dev.iter().any(|commit| commit == &main);
|
let dev_has_main = commit_histories.dev.iter().any(|commit| commit == &main);
|
||||||
if !dev_has_main {
|
if !dev_has_main {
|
||||||
|
@ -102,21 +127,23 @@ pub async fn validate_positions(
|
||||||
repo_config.branches().main(),
|
repo_config.branches().main(),
|
||||||
repo_config.branches().main(),
|
repo_config.branches().main(),
|
||||||
);
|
);
|
||||||
return;
|
return Err(Error::DevBranchNotBasedOn(repo_config.branches().main()));
|
||||||
}
|
}
|
||||||
let Some(dev) = commit_histories.dev.first().cloned() else {
|
let Some(dev) = commit_histories.dev.first().cloned() else {
|
||||||
warn!(
|
warn!(
|
||||||
"No commits on dev branch '{}'",
|
"No commits on dev branch '{}'",
|
||||||
repo_config.branches().dev()
|
repo_config.branches().dev()
|
||||||
);
|
);
|
||||||
return;
|
return Err(Error::BranchHasNoCommits(repo_config.branches().dev()));
|
||||||
};
|
};
|
||||||
addr.do_send(StartMonitoring {
|
info!("Validation - OK");
|
||||||
|
|
||||||
|
Ok(ValidatedPositions {
|
||||||
main,
|
main,
|
||||||
next,
|
next,
|
||||||
dev,
|
dev,
|
||||||
dev_commit_history: commit_histories.dev,
|
dev_commit_history: commit_histories.dev,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_commit_histories(
|
async fn get_commit_histories(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use kxio::network::{self, Network};
|
use kxio::network::{self, Network};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
use crate::server::{
|
use crate::server::{
|
||||||
config::{BranchName, RepoDetails},
|
config::{BranchName, RepoDetails},
|
||||||
|
@ -21,7 +21,7 @@ pub(super) async fn contents_get(
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/contents/{file_path}?ref={branch}&token={token}"
|
"https://{hostname}/api/v1/repos/{repo_path}/contents/{file_path}?ref={branch}&token={token}"
|
||||||
));
|
));
|
||||||
|
|
||||||
info!(%url, "Loading config");
|
// info!("Loading config");
|
||||||
let request = network::NetRequest::new(
|
let request = network::NetRequest::new(
|
||||||
network::RequestMethod::Get,
|
network::RequestMethod::Get,
|
||||||
url,
|
url,
|
||||||
|
|
|
@ -2,20 +2,22 @@ pub mod branch;
|
||||||
mod file;
|
mod file;
|
||||||
mod repo;
|
mod repo;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
|
||||||
use kxio::network::{self, Network};
|
use kxio::network::{self, Network};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
use crate::server::{
|
use crate::server::{
|
||||||
actors::repo::RepoActor,
|
actors::repo::{RepoActor, StartMonitoring, ValidateRepo},
|
||||||
config::{BranchName, GitDir, RepoConfig, RepoDetails},
|
config::{BranchName, GitDir, RepoConfig, RepoDetails},
|
||||||
gitforge::{self, RepoCloneError},
|
gitforge::{self, forgejo::branch::ValidatedPositions, RepoCloneError},
|
||||||
types::GitRef,
|
types::GitRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ForgeJo;
|
struct ForgeJo;
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ForgeJoEnv {
|
pub struct ForgeJoEnv {
|
||||||
repo_details: RepoDetails,
|
repo_details: RepoDetails,
|
||||||
net: Network,
|
net: Network,
|
||||||
|
@ -44,7 +46,26 @@ impl super::ForgeLike for ForgeJoEnv {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn branches_validate_positions(&self, repo_config: RepoConfig, addr: Addr<RepoActor>) {
|
async fn branches_validate_positions(&self, repo_config: RepoConfig, addr: Addr<RepoActor>) {
|
||||||
branch::validate_positions(self, repo_config, addr).await
|
match branch::validate_positions(self, repo_config).await {
|
||||||
|
Ok(ValidatedPositions {
|
||||||
|
main,
|
||||||
|
next,
|
||||||
|
dev,
|
||||||
|
dev_commit_history,
|
||||||
|
}) => {
|
||||||
|
addr.do_send(StartMonitoring {
|
||||||
|
main,
|
||||||
|
next,
|
||||||
|
dev,
|
||||||
|
dev_commit_history,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!("{}", err);
|
||||||
|
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||||
|
addr.do_send(ValidateRepo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn branch_reset(
|
fn branch_reset(
|
||||||
|
@ -102,16 +123,15 @@ impl super::ForgeLike for ForgeJoEnv {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError> {
|
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError> {
|
||||||
if gitdir.exists() {
|
if !gitdir.exists() {
|
||||||
info!(%gitdir, "Gitdir already exists - validating...");
|
info!("Local copy not found - cloning...");
|
||||||
|
repo::clone(&self.repo_details, gitdir.clone())?;
|
||||||
|
}
|
||||||
|
info!("Validating...");
|
||||||
gitdir
|
gitdir
|
||||||
.validate(&self.repo_details)
|
.validate(&self.repo_details)
|
||||||
.map_err(|e| RepoCloneError::Validation(e.to_string()))
|
.map_err(|e| RepoCloneError::Validation(e.to_string()))
|
||||||
.inspect(|_| info!(%gitdir, "Validation - OK"))
|
.inspect(|_| info!("Validation - OK"))
|
||||||
} else {
|
|
||||||
info!(%gitdir, "Gitdir doesn't exists - cloning...");
|
|
||||||
repo::clone(&self.repo_details, gitdir).inspect(|_| info!("Cloned - OK"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,14 @@ use crate::server::{
|
||||||
gitforge::RepoCloneError,
|
gitforge::RepoCloneError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn clone(repo_details: &RepoDetails, gitdir: GitDir) -> Result<(), RepoCloneError> {
|
pub fn clone(repo_details: &RepoDetails, gitdir: GitDir) -> Result<(), RepoCloneError> {
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
let origin = repo_details.origin();
|
let origin = repo_details.origin();
|
||||||
info!("Cloning");
|
let (_repository, _outcome) =
|
||||||
let (repository, _outcome) =
|
|
||||||
gix::prepare_clone_bare(origin.expose_secret().as_str(), gitdir.deref())?
|
gix::prepare_clone_bare(origin.expose_secret().as_str(), gitdir.deref())?
|
||||||
.fetch_only(gix::progress::Discard, &AtomicBool::new(false))?;
|
.fetch_only(gix::progress::Discard, &AtomicBool::new(false))?;
|
||||||
info!(?repository, "Cloned");
|
info!("Cloned - OK");
|
||||||
|
|
||||||
Ok(()) // TODO: (#69) return Repository inside a newtype to store in the RepoActor for reuse else where
|
Ok(()) // TODO: (#69) return Repository inside a newtype to store in the RepoActor for reuse else where
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::server::{
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MockForge;
|
struct MockForge;
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MockForgeEnv;
|
pub struct MockForgeEnv;
|
||||||
impl MockForgeEnv {
|
impl MockForgeEnv {
|
||||||
pub(crate) const fn new() -> Self {
|
pub(crate) const fn new() -> Self {
|
||||||
|
|
|
@ -58,7 +58,7 @@ pub trait ForgeLike {
|
||||||
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError>;
|
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Forge {
|
pub enum Forge {
|
||||||
Mock(mock_forge::MockForgeEnv),
|
Mock(mock_forge::MockForgeEnv),
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
|
|
@ -69,24 +69,34 @@ pub async fn start(fs: FileSystem, net: Network) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// create data dir if missing
|
// Server Storage
|
||||||
let dir = server_config.storage().path();
|
let dir = server_config.storage().path();
|
||||||
if !dir.exists() {
|
if !dir.exists() {
|
||||||
|
info!(?dir, "server storage doesn't exist - creating it");
|
||||||
if let Err(err) = fs.dir_create(dir) {
|
if let Err(err) = fs.dir_create(dir) {
|
||||||
error!(?err, ?dir, "Failed to create server storage directory");
|
error!(?err, ?dir, "Failed to create server storage");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let Ok(canon) = dir.canonicalize() else {
|
||||||
|
error!(?dir, "Failed to confirm servier storage");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
info!(dir = ?canon, "server storage");
|
||||||
|
|
||||||
|
// Forge directories in Server Storage
|
||||||
|
let server_storage = server_config.storage();
|
||||||
|
if let Err(err) = create_forge_data_directories(&server_config, &fs, &dir) {
|
||||||
|
error!(?err, "Failure creating forge storage");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Webhook Server
|
||||||
|
info!("Starting Webhook Server...");
|
||||||
let webhook_router = webhook::WebhookRouter::new().start();
|
let webhook_router = webhook::WebhookRouter::new().start();
|
||||||
let webhook = server_config.webhook();
|
let webhook = server_config.webhook();
|
||||||
let server_storage = server_config.storage();
|
|
||||||
|
|
||||||
if let Err(err) = create_forge_data_directories(&server_config, &fs, &dir) {
|
|
||||||
error!(?err, "Failure creating forge data directories");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Forge Actors
|
||||||
for (forge_name, forge_config) in server_config.forges() {
|
for (forge_name, forge_config) in server_config.forges() {
|
||||||
create_forge_repos(
|
create_forge_repos(
|
||||||
forge_config,
|
forge_config,
|
||||||
|
@ -100,7 +110,9 @@ pub async fn start(fs: FileSystem, net: Network) {
|
||||||
.map(|(alias, addr)| webhook::AddWebhookRecipient(alias, addr.recipient()))
|
.map(|(alias, addr)| webhook::AddWebhookRecipient(alias, addr.recipient()))
|
||||||
.for_each(|msg| webhook_router.do_send(msg));
|
.for_each(|msg| webhook_router.do_send(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
let webhook_server = webhook::WebhookActor::new(webhook_router.recipient()).start();
|
let webhook_server = webhook::WebhookActor::new(webhook_router.recipient()).start();
|
||||||
|
info!("Server running - Press Ctrl-C to stop...");
|
||||||
let _ = actix_rt::signal::ctrl_c().await;
|
let _ = actix_rt::signal::ctrl_c().await;
|
||||||
info!("Ctrl-C received, shutting down...");
|
info!("Ctrl-C received, shutting down...");
|
||||||
drop(webhook_server);
|
drop(webhook_server);
|
||||||
|
@ -119,6 +131,7 @@ fn create_forge_data_directories(
|
||||||
return Err(Error::ForgeDirIsNotDirectory { path });
|
return Err(Error::ForgeDirIsNotDirectory { path });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
info!(%forge_name, ?path, "creating storage");
|
||||||
fs.dir_create_all(&path)?;
|
fs.dir_create_all(&path)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +146,8 @@ fn create_forge_repos(
|
||||||
webhook: &Webhook,
|
webhook: &Webhook,
|
||||||
net: &Network,
|
net: &Network,
|
||||||
) -> Vec<(ForgeName, RepoAlias, RepoActor)> {
|
) -> Vec<(ForgeName, RepoAlias, RepoActor)> {
|
||||||
let span = tracing::info_span!("Forge", %forge_name, %forge_config);
|
let span =
|
||||||
|
tracing::info_span!("create_forge_repos", name = %forge_name, config = %forge_config);
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
info!("Creating Forge");
|
info!("Creating Forge");
|
||||||
let mut repos = vec![];
|
let mut repos = vec![];
|
||||||
|
@ -147,9 +161,8 @@ fn create_forge_repos(
|
||||||
for (repo_alias, server_repo_config) in forge_config.repos() {
|
for (repo_alias, server_repo_config) in forge_config.repos() {
|
||||||
let forge_repo = creator((repo_alias, server_repo_config));
|
let forge_repo = creator((repo_alias, server_repo_config));
|
||||||
info!(
|
info!(
|
||||||
forge = %forge_repo.0,
|
|
||||||
alias = %forge_repo.1,
|
alias = %forge_repo.1,
|
||||||
"created forge repo"
|
"Created Repo"
|
||||||
);
|
);
|
||||||
repos.push(forge_repo);
|
repos.push(forge_repo);
|
||||||
}
|
}
|
||||||
|
@ -166,8 +179,9 @@ fn create_actor(
|
||||||
let server_storage = server_storage.clone();
|
let server_storage = server_storage.clone();
|
||||||
let webhook = webhook.clone();
|
let webhook = webhook.clone();
|
||||||
let net = net.clone();
|
let net = net.clone();
|
||||||
move |(repo_name, server_repo_config)| {
|
move |(repo_alias, server_repo_config)| {
|
||||||
let span = tracing::info_span!("Repo", %repo_name, %server_repo_config);
|
let span =
|
||||||
|
tracing::info_span!("create_actor", alias = %repo_alias, config = %server_repo_config);
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
info!("Creating Repo");
|
info!("Creating Repo");
|
||||||
let gitdir = server_repo_config.gitdir().map_or_else(
|
let gitdir = server_repo_config.gitdir().map_or_else(
|
||||||
|
@ -176,7 +190,7 @@ fn create_actor(
|
||||||
server_storage
|
server_storage
|
||||||
.path()
|
.path()
|
||||||
.join(forge_name.to_string())
|
.join(forge_name.to_string())
|
||||||
.join(repo_name.to_string()),
|
.join(repo_alias.to_string()),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|gitdir| gitdir,
|
|gitdir| gitdir,
|
||||||
|
@ -184,15 +198,15 @@ fn create_actor(
|
||||||
// INFO: can't canonicalise gitdir as the path needs to exist to do that and we may not
|
// INFO: can't canonicalise gitdir as the path needs to exist to do that and we may not
|
||||||
// have cloned the repo yet
|
// have cloned the repo yet
|
||||||
let repo_details = config::RepoDetails::new(
|
let repo_details = config::RepoDetails::new(
|
||||||
&repo_name,
|
&repo_alias,
|
||||||
server_repo_config,
|
server_repo_config,
|
||||||
&forge_name,
|
&forge_name,
|
||||||
&forge_config,
|
&forge_config,
|
||||||
gitdir,
|
gitdir,
|
||||||
);
|
);
|
||||||
|
info!("Starting Repo Actor");
|
||||||
let actor = actors::repo::RepoActor::new(repo_details, webhook.clone(), net.clone());
|
let actor = actors::repo::RepoActor::new(repo_details, webhook.clone(), net.clone());
|
||||||
info!("Created Repo");
|
(forge_name.clone(), repo_alias, actor)
|
||||||
(forge_name.clone(), repo_name, actor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +214,7 @@ fn start_actor(
|
||||||
actor: (ForgeName, RepoAlias, actors::repo::RepoActor),
|
actor: (ForgeName, RepoAlias, actors::repo::RepoActor),
|
||||||
) -> (RepoAlias, Addr<actors::repo::RepoActor>) {
|
) -> (RepoAlias, Addr<actors::repo::RepoActor>) {
|
||||||
let (forge_name, repo_alias, actor) = actor;
|
let (forge_name, repo_alias, actor) = actor;
|
||||||
let span = tracing::info_span!("Forge/Repo", %forge_name, %repo_alias);
|
let span = tracing::info_span!("start_actor", forge = %forge_name, repo = %repo_alias);
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
info!("Starting");
|
info!("Starting");
|
||||||
let addr = actor.start();
|
let addr = actor.start();
|
||||||
|
|
Loading…
Reference in a new issue