Compare commits

..

3 commits

Author SHA1 Message Date
89975894a4 feat: terminate process if config file is invalid
Some checks failed
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
Rust / build (push) Failing after 1m14s
2024-07-30 11:57:39 +01:00
1650e93920 feat: return better errors to user on server failure
All checks were successful
Rust / build (push) Successful in 1m14s
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
2024-07-30 11:18:29 +01:00
9a9c73d929 feat: return better errors to the user on init
All checks were successful
Rust / build (push) Successful in 1m15s
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
2024-07-30 11:18:29 +01:00
10 changed files with 67 additions and 67 deletions

View file

@ -80,6 +80,7 @@ derive_more = { version = "1.0.0-beta.6", features = [
"from",
] }
derive-with = "0.5"
anyhow = "1.0"
thiserror = "1.0"
pike = "0.1"

View file

@ -50,6 +50,7 @@ actix-rt = { workspace = true }
# boilerplate
derive_more = { workspace = true }
derive-with = { workspace = true }
anyhow = { workspace = true }
thiserror = { workspace = true }
# Webhooks

View file

@ -2,6 +2,7 @@
use actix::prelude::*;
use actix::Recipient;
use anyhow::{Context, Result};
use notify::event::ModifyKind;
use notify::Watcher;
use tracing::error;
@ -19,15 +20,13 @@ pub enum Error {
#[error("io")]
Io(#[from] std::io::Error),
}
pub async fn watch_file(path: PathBuf, recipient: Recipient<FileUpdated>) {
pub async fn watch_file(path: PathBuf, recipient: Recipient<FileUpdated>) -> Result<()> {
let (tx, rx) = std::sync::mpsc::channel();
#[allow(clippy::expect_used)]
let mut handler = notify::recommended_watcher(tx).expect("file watcher");
#[allow(clippy::expect_used)]
let mut handler = notify::recommended_watcher(tx).context("file watcher")?;
handler
.watch(&path, notify::RecursiveMode::NonRecursive)
.expect("watch file");
.with_context(|| format!("Watch file: {path:?}"))?;
info!("Watching: {:?}", path);
async move {
loop {
@ -55,4 +54,5 @@ pub async fn watch_file(path: PathBuf, recipient: Recipient<FileUpdated>) {
}
}
.await;
Ok(())
}

View file

@ -1,29 +1,18 @@
//
use anyhow::{Context, Result};
use kxio::fs::FileSystem;
pub fn run(fs: FileSystem) {
let file_name = ".git-next.toml";
let pathbuf = fs.base().join(file_name);
match fs.path_exists(&pathbuf) {
Ok(exists) => {
if exists {
eprintln!(
"The configuration file already exists at {} - not overwritting it.",
file_name
);
pub fn run(fs: FileSystem) -> Result<()> {
let pathbuf = fs.base().join(".git-next.toml");
if fs
.path_exists(&pathbuf)
.with_context(|| format!("Checking for existing file: {pathbuf:?}"))?
{
eprintln!("The configuration file already exists at {pathbuf:?} - not overwritting it.",);
} else {
match fs.file_write(&pathbuf, include_str!("../default.toml")) {
Ok(_) => {
println!("Created a default configuration file at {}", file_name);
}
Err(e) => {
eprintln!("Failed to write to the configuration file: {}", e)
}
}
}
}
Err(err) => {
eprintln!("Could not check if file exist: {} - {err:?}", file_name);
}
fs.file_write(&pathbuf, include_str!("../default.toml"))
.with_context(|| format!("Writing file: {pathbuf:?}"))?;
println!("Created a default configuration file at {pathbuf:?}");
}
Ok(())
}

View file

@ -13,6 +13,7 @@ use git_next_core::git;
use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use kxio::{fs, network::Network};
@ -34,7 +35,7 @@ enum Server {
Start,
}
fn main() {
fn main() -> Result<()> {
let fs = fs::new(PathBuf::default());
let net = Network::new_real();
let repository_factory = git::repository::factory::real();
@ -42,16 +43,17 @@ fn main() {
match commands.command {
Command::Init => {
init::run(fs);
init::run(fs)?;
}
Command::Server(server) => match server {
Server::Init => {
server::init(fs);
server::init(fs)?;
}
Server::Start => {
let sleep_duration = std::time::Duration::from_secs(10);
server::start(fs, net, repository_factory, sleep_duration);
server::start(fs, net, repository_factory, sleep_duration)?;
}
},
}
Ok(())
}

View file

@ -12,13 +12,13 @@ impl Handler<FileUpdated> for ServerActor {
type Result = ();
fn handle(&mut self, _msg: FileUpdated, ctx: &mut Self::Context) -> Self::Result {
let server_config = match ServerConfig::load(&self.fs) {
Ok(server_config) => server_config,
Err(err) => {
tracing::error!("Failed to load config file. Error: {}", err);
return;
}
};
match ServerConfig::load(&self.fs) {
Ok(server_config) => {
self.do_send(ReceiveServerConfig::new(server_config), ctx);
}
Err(err) => {
self.abort(ctx, format!("Failed to load config file. Error: {}", err));
}
};
}
}

View file

@ -12,18 +12,15 @@ impl Handler<ReceiveServerConfig> for ServerActor {
fn handle(&mut self, msg: ReceiveServerConfig, ctx: &mut Self::Context) -> Self::Result {
tracing::info!("recieved server config");
let Ok(socket_addr) = msg.http() else {
tracing::error!("Unable to parse http.addr");
return;
self.abort(ctx, "Unable to parse http.addr");
};
let Some(server_storage) = self.server_storage(&msg) else {
tracing::error!("Server storage not available");
return;
self.abort(ctx, "Server storage not available");
};
if msg.inbound_webhook().base_url().ends_with('/') {
tracing::error!("webhook.url must not end with a '/'");
return;
self.abort(ctx, "webhook.url must not end with a '/'");
}
self.do_send(

View file

@ -229,6 +229,19 @@ impl ServerActor {
Some(server_storage)
}
/// Attempts to gracefully shutdown the server before terminating the process.
fn abort(
&mut self,
ctx: &mut <Self as actix::Actor>::Context,
message: impl Into<String>,
) -> ! {
tracing::error!("Aborting: {}", message.into());
self.do_send(crate::server::actor::messages::Shutdown, ctx);
std::thread::sleep(std::time::Duration::from_millis(200));
System::current().stop();
std::process::exit(-1);
}
fn do_send<M>(&mut self, msg: M, _ctx: &mut <Self as actix::Actor>::Context)
where
M: actix::Message + Send + 'static + std::fmt::Debug,

View file

@ -10,31 +10,26 @@ use crate::file_watcher::{watch_file, FileUpdated};
use actor::ServerActor;
use git_next_core::git::RepositoryFactory;
use anyhow::{Context, Result};
use kxio::{fs::FileSystem, network::Network};
use tracing::info;
use std::path::PathBuf;
pub fn init(fs: FileSystem) {
pub fn init(fs: FileSystem) -> Result<()> {
let file_name = "git-next-server.toml";
let pathbuf = PathBuf::from(file_name);
let Ok(exists) = fs.path_exists(&pathbuf) else {
eprintln!("Could not check if file exist: {}", file_name);
return;
};
if exists {
eprintln!(
"The configuration file already exists at {} - not overwritting it.",
file_name
);
if fs
.path_exists(&pathbuf)
.with_context(|| format!("Checking for existing file: {pathbuf:?}"))?
{
eprintln!("The configuration file already exists at {pathbuf:?} - not overwritting it.",);
} else {
match fs.file_write(&pathbuf, include_str!("server-default.toml")) {
Ok(_) => println!("Created a default configuration file at {}", file_name),
Err(e) => {
eprintln!("Failed to write to the configuration file: {}", e)
}
}
fs.file_write(&pathbuf, include_str!("server-default.toml"))
.with_context(|| format!("Writing file: {pathbuf:?}"))?;
println!("Created a default configuration file at {pathbuf:?}",);
}
Ok(())
}
pub fn start(
@ -42,7 +37,7 @@ pub fn start(
net: Network,
repo: Box<dyn RepositoryFactory>,
sleep_duration: std::time::Duration,
) {
) -> Result<()> {
init_logging();
let execution = async move {
@ -51,7 +46,10 @@ pub fn start(
server.do_send(FileUpdated);
info!("Starting File Watcher...");
watch_file("git-next-server.toml".into(), server.clone().recipient()).await;
#[allow(clippy::expect_used)]
watch_file("git-next-server.toml".into(), server.clone().recipient())
.await
.expect("file watcher");
info!("Server running - Press Ctrl-C to stop...");
let _ = actix_rt::signal::ctrl_c().await;
@ -62,9 +60,8 @@ pub fn start(
};
let system = System::new();
Arbiter::current().spawn(execution);
if let Err(err) = system.run() {
tracing::error!(?err, "")
};
system.run()?;
Ok(())
}
pub fn init_logging() {

View file

@ -8,7 +8,7 @@ mod init {
let file = fs.base().join(".git-next.toml");
fs.file_write(&file, "contents")?;
crate::init::run(fs.clone());
crate::init::run(fs.clone())?;
assert_eq!(
fs.file_read_to_string(&file)?,
@ -23,7 +23,7 @@ mod init {
fn should_create_default_file_if_not_exists() -> TestResult {
let fs = kxio::fs::temp()?;
crate::init::run(fs.clone());
crate::init::run(fs.clone())?;
let file = fs.base().join(".git-next.toml");