chore(logging): clean up and reformat logging
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful

This commit is contained in:
Paul Campbell 2024-05-04 12:37:35 +01:00
parent 5ba5a126c3
commit 7516ec1dc1
18 changed files with 209 additions and 118 deletions

View file

@ -1,5 +1,4 @@
steps:
todo_check:
# INFO: https://woodpecker-ci.org/plugins/TODO-Checker
image: codeberg.org/epsilon_02/todo-checker:1.1
@ -8,7 +7,7 @@ steps:
branch: next
settings:
# git-next-woodpecker-todo-checker - read:issue
repository_token: '776a3b928b852472c2af727a360c85c00af64b9f'
repository_token: "776a3b928b852472c2af727a360c85c00af64b9f"
prefix_regex: "(#|//) (TODO|FIXME): "
debug: false
@ -21,7 +20,7 @@ steps:
CARGO_TERM_COLOR: always
commands:
- 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
test:

View file

@ -75,7 +75,7 @@ anyhow = "1.0"
cc-cli = { version = "0.1" }
[lints.clippy]
nursery = "warn"
nursery = { level = "warn", priority = -1 }
# pedantic = "warn"
unwrap_used = "warn"
expect_used = "warn"

View file

@ -1,18 +1,25 @@
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};
/// 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) {
let repo_config = match crate::server::config::load::load(&repo_details, &forge).await {
#[tracing::instrument(skip_all, fields(branch = %repo_details.branch))]
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,
Err(err) => {
error!(?err, "Failed to load config");
return;
}
};
info!("Loaded .git-next.toml from repo");
addr.do_send(LoadedConfig(repo_config));
}

View file

@ -5,7 +5,7 @@ pub mod webhook;
use actix::prelude::*;
use kxio::network::Network;
use tracing::{info, warn};
use tracing::{debug, info, warn, Instrument};
use crate::server::{
actors::repo::webhook::WebhookAuth,
@ -16,6 +16,7 @@ use crate::server::{
use self::webhook::WebhookId;
pub struct RepoActor {
span: tracing::Span,
details: RepoDetails,
webhook: Webhook,
webhook_id: Option<WebhookId>, // INFO: if [None] then no webhook is configured
@ -28,6 +29,7 @@ pub struct RepoActor {
}
impl RepoActor {
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 {
#[cfg(feature = "forgejo")]
crate::server::config::ForgeType::ForgeJo => {
@ -36,7 +38,9 @@ impl RepoActor {
#[cfg(test)]
crate::server::config::ForgeType::MockForge => gitforge::Forge::new_mock(),
};
debug!(?forge, "new");
Self {
span,
details,
webhook,
webhook_id: None,
@ -52,11 +56,14 @@ impl RepoActor {
impl Actor for RepoActor {
type Context = Context<Self>;
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();
webhook::unregister(webhook_id, repo_details, net)
.in_current_span()
.into_actor(self)
.wait(ctx);
Running::Continue
@ -69,7 +76,7 @@ impl std::fmt::Display for RepoActor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"RepoActor: {}/{}",
"{}/{}",
self.details.forge.forge_name, self.details.repo_alias
)
}
@ -80,9 +87,9 @@ impl std::fmt::Display for RepoActor {
pub struct CloneRepo;
impl Handler<CloneRepo> for RepoActor {
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 {
info!(%self.details, "Clone/Update Repo");
info!("Message Received");
let gitdir = self.details.gitdir.clone();
match self.forge.repo_clone(gitdir) {
Ok(_) => ctx.address().do_send(LoadConfigFromRepo),
@ -96,13 +103,14 @@ impl Handler<CloneRepo> for RepoActor {
pub struct LoadConfigFromRepo;
impl Handler<LoadConfigFromRepo> for RepoActor {
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 {
info!(%self.details, "Loading .git-next.toml from repo");
info!("Message Received");
let details = self.details.clone();
let addr = ctx.address();
let forge = self.forge.clone();
config::load(details, addr, forge)
.in_current_span()
.into_actor(self)
.wait(ctx);
}
@ -113,10 +121,10 @@ impl Handler<LoadConfigFromRepo> for RepoActor {
struct LoadedConfig(pub RepoConfig);
impl Handler<LoadedConfig> for RepoActor {
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 {
info!("Message Received");
let repo_config = msg.0;
info!(%self.details, %repo_config, "Config loaded");
self.details.repo_config.replace(repo_config);
if self.webhook_id.is_none() {
webhook::register(
@ -125,6 +133,7 @@ impl Handler<LoadedConfig> for RepoActor {
ctx.address(),
self.net.clone(),
)
.in_current_span()
.into_actor(self)
.wait(ctx);
}
@ -137,13 +146,14 @@ impl Handler<LoadedConfig> for RepoActor {
pub struct ValidateRepo;
impl Handler<ValidateRepo> for RepoActor {
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 {
info!("ValidateRepo");
info!("Message Received");
if let Some(repo_config) = self.details.repo_config.clone() {
let forge = self.forge.clone();
let addr = ctx.address();
async move { forge.branches_validate_positions(repo_config, addr).await }
.in_current_span()
.into_actor(self)
.wait(ctx);
}
@ -160,9 +170,9 @@ pub struct StartMonitoring {
}
impl Handler<StartMonitoring> for RepoActor {
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 {
info!("StartMonitoring");
info!("Message Received");
let Some(repo_config) = self.details.repo_config.clone() else {
warn!("No config loaded");
return;
@ -170,7 +180,7 @@ impl Handler<StartMonitoring> for RepoActor {
let next_ahead_of_main = msg.main != msg.next;
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 webhook = self.webhook.clone();
@ -180,14 +190,17 @@ impl Handler<StartMonitoring> for RepoActor {
if next_ahead_of_main {
status::check_next(msg.next, addr, forge)
.in_current_span()
.into_actor(self)
.wait(ctx);
} else if dev_ahead_of_next {
branch::advance_next(msg.next, msg.dev_commit_history, repo_config, forge, addr)
.in_current_span()
.into_actor(self)
.wait(ctx);
} else if self.webhook_id.is_none() {
webhook::register(repo_details, webhook, addr, net)
.in_current_span()
.into_actor(self)
.wait(ctx);
}
@ -199,8 +212,9 @@ impl Handler<StartMonitoring> for RepoActor {
pub struct WebhookRegistered(pub WebhookId, pub WebhookAuth);
impl Handler<WebhookRegistered> for RepoActor {
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 {
info!("Message Received");
self.webhook_id.replace(msg.0);
self.webhook_auth.replace(msg.1);
}
@ -211,8 +225,9 @@ impl Handler<WebhookRegistered> for RepoActor {
pub struct AdvanceMainTo(pub gitforge::Commit);
impl Handler<AdvanceMainTo> for RepoActor {
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 {
info!("Message Received");
let Some(repo_config) = self.details.repo_config.clone() else {
warn!("No config loaded");
return;
@ -220,6 +235,7 @@ impl Handler<AdvanceMainTo> for RepoActor {
let forge = self.forge.clone();
let addr = ctx.address();
branch::advance_main(msg.0, repo_config, forge, addr)
.in_current_span()
.into_actor(self)
.wait(ctx);
}

View file

@ -57,12 +57,12 @@ impl Deref for WebhookAuth {
}
}
#[tracing::instrument(skip_all, fields(%webhook_id))]
pub async fn unregister(
webhook_id: WebhookId,
repo_details: crate::server::config::RepoDetails,
net: network::Network,
) {
info!(?webhook_id, "unregister webhook");
let hostname = &repo_details.forge.hostname;
let repo_path = repo_details.repo_path;
use secrecy::ExposeSecret;
@ -81,11 +81,12 @@ pub async fn unregister(
);
let result = net.delete(request).await;
match result {
Ok(_) => info!(?webhook_id, "unregistered webhook"),
Err(err) => warn!(?webhook_id, ?err, "Failed to unregister webhook"),
Ok(_) => info!("unregistered webhook"),
Err(err) => warn!(?err, "Failed to unregister webhook"),
}
}
#[tracing::instrument(skip_all)]
pub async fn register(
repo_details: crate::server::config::RepoDetails,
webhook: Webhook,
@ -103,7 +104,6 @@ pub async fn register(
unregister(webhook_id, repo_details.clone(), net.clone()).await;
}
info!("Registering webhook");
let hostname = &repo_details.forge.hostname;
let repo_path = repo_details.repo_path;
use secrecy::ExposeSecret;
@ -138,7 +138,7 @@ pub async fn register(
match result {
Ok(response) => {
if let Some(hook) = response.response_body() {
info!("Webhook registered");
info!(webhook_id = %hook.id, "Webhook registered");
addr.do_send(WebhookRegistered(hook.id(), authorisation));
}
}

View file

@ -9,15 +9,19 @@ use actix::prelude::*;
pub use message::WebhookMessage;
pub use router::AddWebhookRecipient;
pub use router::WebhookRouter;
use tracing::Instrument;
#[derive(Debug)]
pub struct WebhookActor {
span: tracing::Span,
spawn_handle: Option<actix::SpawnHandle>,
message_receiver: Recipient<WebhookMessage>,
}
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 {
span,
message_receiver,
spawn_handle: None,
}
@ -26,9 +30,10 @@ impl WebhookActor {
impl Actor for WebhookActor {
type Context = actix::Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
let _gaurd = self.span.enter();
let address: Recipient<WebhookMessage> = self.message_receiver.clone();
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);
}
}

View file

@ -1,17 +1,21 @@
use std::collections::HashMap;
use actix::prelude::*;
use tracing::debug;
use tracing::{debug, info};
use crate::server::{actors::webhook::message::WebhookMessage, config::RepoAlias};
#[derive(Default)]
pub struct WebhookRouter {
span: tracing::Span,
repos: HashMap<RepoAlias, Recipient<WebhookMessage>>,
}
impl WebhookRouter {
pub fn new() -> Self {
Self::default()
let span = tracing::info_span!("WebhookRouter");
Self {
span,
repos: Default::default(),
}
}
}
impl Actor for WebhookRouter {
@ -22,10 +26,11 @@ impl Handler<WebhookMessage> for WebhookRouter {
type 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());
debug!(?repo_alias, "Router...");
debug!(repo = %repo_alias, "Router...");
if let Some(recipient) = self.repos.get(&repo_alias) {
debug!("Sending to recipient");
info!("Sending to Recipient");
recipient.do_send(msg);
}
}
@ -38,6 +43,8 @@ impl Handler<AddWebhookRecipient> for WebhookRouter {
type 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);
}
}

View file

@ -1,4 +1,3 @@
use kxio::network;
use terrors::OneOf;
use tracing::error;
@ -7,17 +6,6 @@ use crate::server::{
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(
details: &RepoDetails,
forge: &gitforge::Forge,

View file

@ -30,8 +30,11 @@ pub struct ServerConfig {
forge: HashMap<String, ForgeConfig>,
}
impl ServerConfig {
#[tracing::instrument(skip_all)]
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)
}
@ -104,7 +107,7 @@ impl RepoConfig {
}
impl Display for RepoConfig {
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 {
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 {
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 {
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 {
write!(
f,
"{}/{} ({}): {}:{}/{} @ {}",
"{}:{}/{}:{}@{}/{}@{}",
self.forge.forge_type,
self.forge.forge_name,
self.repo_alias,
self.forge.forge_type,
self.forge.hostname,
self.forge.user,
self.forge.hostname,
self.repo_path,
self.branch,
)
@ -468,7 +477,7 @@ pub enum ForgeType {
}
impl Display for ForgeType {
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
}
#[tracing::instrument(skip_all)]
pub fn validate(&self, repo_details: &RepoDetails) -> ValidationResult<()> {
let git_remote = repo_details.git_remote();
use gix::remote::Direction;
let push_remote = self.find_default_remote(Direction::Push)?;
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 {
return Err(RepoValidationError::MismatchDefaultPushRemote {
found: push_remote,
@ -506,7 +516,7 @@ impl GitDir {
Ok(())
}
#[tracing::instrument]
#[tracing::instrument(skip_all, fields(?direction))]
fn find_default_remote(
&self,
direction: gix::remote::Direction,
@ -514,7 +524,6 @@ impl GitDir {
let repository = gix::ThreadSafeRepository::open(self.deref())
.map_err(|e| RepoValidationError::UnableToOpenRepo(e.to_string()))?
.to_thread_local();
info!(?repository, from = ?self.deref(), "gix::discover");
let Some(Ok(remote)) = repository.find_default_remote(gix::remote::Direction::Push) else {
return Err(RepoValidationError::NoDefaultPushRemote);
};
@ -548,13 +557,11 @@ impl std::fmt::Display for GitDir {
}
impl From<&str> for GitDir {
fn from(value: &str) -> Self {
info!("GitDir::from::<&str>({value:?})");
Self(value.into())
}
}
impl From<PathBuf> for GitDir {
fn from(value: PathBuf) -> Self {
info!("GitDir::from::<PathBuf>({value:?})");
Self(value)
}
}

View file

@ -1,5 +1,5 @@
use kxio::network::{self, Network};
use tracing::{error, info};
use tracing::error;
use crate::server::{
config::{BranchName, RepoDetails},
@ -18,7 +18,7 @@ pub async fn get_all(
"https://{hostname}/api/v1/repos/{repo_path}/branches?token={token}"
));
info!(%url, "Listing branches");
// info!(%url, "Listing branches");
let request = network::NetRequest::new(
network::RequestMethod::Get,
url,

View file

@ -7,3 +7,4 @@ pub use fetch::fetch;
pub use get_all::get_all;
pub use reset::reset;
pub use validate_positions::validate_positions;
pub use validate_positions::ValidatedPositions;

View file

@ -1,20 +1,37 @@
use actix::prelude::*;
use kxio::network;
use tracing::{debug, error, info, warn};
use crate::server::{
self,
actors::repo::{RepoActor, StartMonitoring},
config::{BranchName, RepoConfig, RepoDetails},
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(
forge: &gitforge::forgejo::ForgeJoEnv,
repo_config: RepoConfig,
addr: Addr<RepoActor>,
) {
) -> Result<ValidatedPositions, Error> {
let repo_details = &forge.repo_details;
// Collect Commit Histories for `main`, `next` and `dev` branches
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,
Err(err) => {
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 '{}'",
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
let Some(next) = commit_histories.next.first().cloned() else {
@ -40,7 +57,7 @@ pub async fn validate_positions(
"No commits on next branch '{}",
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);
if !next_is_ancestor_of_dev {
@ -48,11 +65,15 @@ pub async fn validate_positions(
if let Err(err) = forge.branch_reset(
repo_config.branches().next(),
main.into(),
gitforge::Force::From(next.into()),
gitforge::Force::From(next.clone().into()),
) {
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
@ -69,18 +90,22 @@ pub async fn validate_positions(
if let Err(err) = forge.branch_reset(
repo_config.branches().next(),
main.into(),
gitforge::Force::From(next.into()),
gitforge::Force::From(next.clone().into()),
) {
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 {
warn!(
"No commits on next branch '{}'",
repo_config.branches().next()
);
return;
return Err(Error::BranchHasNoCommits(repo_config.branches().next()));
};
let dev_has_next = commit_histories
.dev
@ -92,7 +117,7 @@ pub async fn validate_positions(
repo_config.branches().dev(),
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);
if !dev_has_main {
@ -102,21 +127,23 @@ pub async fn validate_positions(
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 {
warn!(
"No commits on dev branch '{}'",
repo_config.branches().dev()
);
return;
return Err(Error::BranchHasNoCommits(repo_config.branches().dev()));
};
addr.do_send(StartMonitoring {
info!("Validation - OK");
Ok(ValidatedPositions {
main,
next,
dev,
dev_commit_history: commit_histories.dev,
});
})
}
async fn get_commit_histories(

View file

@ -1,5 +1,5 @@
use kxio::network::{self, Network};
use tracing::{error, info, warn};
use tracing::{error, warn};
use crate::server::{
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}"
));
info!(%url, "Loading config");
// info!("Loading config");
let request = network::NetRequest::new(
network::RequestMethod::Get,
url,

View file

@ -2,20 +2,22 @@ pub mod branch;
mod file;
mod repo;
use std::time::Duration;
use actix::prelude::*;
use kxio::network::{self, Network};
use tracing::{error, info, warn};
use crate::server::{
actors::repo::RepoActor,
actors::repo::{RepoActor, StartMonitoring, ValidateRepo},
config::{BranchName, GitDir, RepoConfig, RepoDetails},
gitforge::{self, RepoCloneError},
gitforge::{self, forgejo::branch::ValidatedPositions, RepoCloneError},
types::GitRef,
};
struct ForgeJo;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ForgeJoEnv {
repo_details: RepoDetails,
net: Network,
@ -44,7 +46,26 @@ impl super::ForgeLike for ForgeJoEnv {
}
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(
@ -102,16 +123,15 @@ impl super::ForgeLike for ForgeJoEnv {
}
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError> {
if gitdir.exists() {
info!(%gitdir, "Gitdir already exists - validating...");
if !gitdir.exists() {
info!("Local copy not found - cloning...");
repo::clone(&self.repo_details, gitdir.clone())?;
}
info!("Validating...");
gitdir
.validate(&self.repo_details)
.map_err(|e| RepoCloneError::Validation(e.to_string()))
.inspect(|_| info!(%gitdir, "Validation - OK"))
} else {
info!(%gitdir, "Gitdir doesn't exists - cloning...");
repo::clone(&self.repo_details, gitdir).inspect(|_| info!("Cloned - OK"))
}
.inspect(|_| info!("Validation - OK"))
}
}

View file

@ -7,14 +7,14 @@ use crate::server::{
gitforge::RepoCloneError,
};
#[tracing::instrument(skip_all)]
pub fn clone(repo_details: &RepoDetails, gitdir: GitDir) -> Result<(), RepoCloneError> {
use secrecy::ExposeSecret;
let origin = repo_details.origin();
info!("Cloning");
let (repository, _outcome) =
let (_repository, _outcome) =
gix::prepare_clone_bare(origin.expose_secret().as_str(), gitdir.deref())?
.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
}

View file

@ -6,7 +6,7 @@ use crate::server::{
};
struct MockForge;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct MockForgeEnv;
impl MockForgeEnv {
pub(crate) const fn new() -> Self {

View file

@ -58,7 +58,7 @@ pub trait ForgeLike {
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError>;
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum Forge {
Mock(mock_forge::MockForgeEnv),
#[allow(clippy::enum_variant_names)]

View file

@ -69,24 +69,34 @@ pub async fn start(fs: FileSystem, net: Network) {
return;
}
};
// create data dir if missing
// Server Storage
let dir = server_config.storage().path();
if !dir.exists() {
info!(?dir, "server storage doesn't exist - creating it");
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;
}
}
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 = 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() {
create_forge_repos(
forge_config,
@ -100,7 +110,9 @@ pub async fn start(fs: FileSystem, net: Network) {
.map(|(alias, addr)| webhook::AddWebhookRecipient(alias, addr.recipient()))
.for_each(|msg| webhook_router.do_send(msg));
}
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;
info!("Ctrl-C received, shutting down...");
drop(webhook_server);
@ -119,6 +131,7 @@ fn create_forge_data_directories(
return Err(Error::ForgeDirIsNotDirectory { path });
}
} else {
info!(%forge_name, ?path, "creating storage");
fs.dir_create_all(&path)?;
}
}
@ -133,7 +146,8 @@ fn create_forge_repos(
webhook: &Webhook,
net: &Network,
) -> 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();
info!("Creating Forge");
let mut repos = vec![];
@ -147,9 +161,8 @@ fn create_forge_repos(
for (repo_alias, server_repo_config) in forge_config.repos() {
let forge_repo = creator((repo_alias, server_repo_config));
info!(
forge = %forge_repo.0,
alias = %forge_repo.1,
"created forge repo"
"Created Repo"
);
repos.push(forge_repo);
}
@ -166,8 +179,9 @@ fn create_actor(
let server_storage = server_storage.clone();
let webhook = webhook.clone();
let net = net.clone();
move |(repo_name, server_repo_config)| {
let span = tracing::info_span!("Repo", %repo_name, %server_repo_config);
move |(repo_alias, server_repo_config)| {
let span =
tracing::info_span!("create_actor", alias = %repo_alias, config = %server_repo_config);
let _guard = span.enter();
info!("Creating Repo");
let gitdir = server_repo_config.gitdir().map_or_else(
@ -176,7 +190,7 @@ fn create_actor(
server_storage
.path()
.join(forge_name.to_string())
.join(repo_name.to_string()),
.join(repo_alias.to_string()),
)
},
|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
// have cloned the repo yet
let repo_details = config::RepoDetails::new(
&repo_name,
&repo_alias,
server_repo_config,
&forge_name,
&forge_config,
gitdir,
);
info!("Starting Repo Actor");
let actor = actors::repo::RepoActor::new(repo_details, webhook.clone(), net.clone());
info!("Created Repo");
(forge_name.clone(), repo_name, actor)
(forge_name.clone(), repo_alias, actor)
}
}
@ -200,7 +214,7 @@ fn start_actor(
actor: (ForgeName, RepoAlias, actors::repo::RepoActor),
) -> (RepoAlias, Addr<actors::repo::RepoActor>) {
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();
info!("Starting");
let addr = actor.start();