Compare commits
3 commits
03ae9153b4
...
89975894a4
Author | SHA1 | Date | |
---|---|---|---|
89975894a4 | |||
1650e93920 | |||
9a9c73d929 |
10 changed files with 67 additions and 67 deletions
|
@ -80,6 +80,7 @@ derive_more = { version = "1.0.0-beta.6", features = [
|
||||||
"from",
|
"from",
|
||||||
] }
|
] }
|
||||||
derive-with = "0.5"
|
derive-with = "0.5"
|
||||||
|
anyhow = "1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
pike = "0.1"
|
pike = "0.1"
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ actix-rt = { workspace = true }
|
||||||
# boilerplate
|
# boilerplate
|
||||||
derive_more = { workspace = true }
|
derive_more = { workspace = true }
|
||||||
derive-with = { workspace = true }
|
derive-with = { workspace = true }
|
||||||
|
anyhow = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
# Webhooks
|
# Webhooks
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
|
||||||
use actix::Recipient;
|
use actix::Recipient;
|
||||||
|
use anyhow::{Context, Result};
|
||||||
use notify::event::ModifyKind;
|
use notify::event::ModifyKind;
|
||||||
use notify::Watcher;
|
use notify::Watcher;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
@ -19,15 +20,13 @@ pub enum Error {
|
||||||
#[error("io")]
|
#[error("io")]
|
||||||
Io(#[from] std::io::Error),
|
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();
|
let (tx, rx) = std::sync::mpsc::channel();
|
||||||
|
|
||||||
#[allow(clippy::expect_used)]
|
let mut handler = notify::recommended_watcher(tx).context("file watcher")?;
|
||||||
let mut handler = notify::recommended_watcher(tx).expect("file watcher");
|
|
||||||
#[allow(clippy::expect_used)]
|
|
||||||
handler
|
handler
|
||||||
.watch(&path, notify::RecursiveMode::NonRecursive)
|
.watch(&path, notify::RecursiveMode::NonRecursive)
|
||||||
.expect("watch file");
|
.with_context(|| format!("Watch file: {path:?}"))?;
|
||||||
info!("Watching: {:?}", path);
|
info!("Watching: {:?}", path);
|
||||||
async move {
|
async move {
|
||||||
loop {
|
loop {
|
||||||
|
@ -55,4 +54,5 @@ pub async fn watch_file(path: PathBuf, recipient: Recipient<FileUpdated>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.await;
|
.await;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,18 @@
|
||||||
//
|
//
|
||||||
|
use anyhow::{Context, Result};
|
||||||
use kxio::fs::FileSystem;
|
use kxio::fs::FileSystem;
|
||||||
|
|
||||||
pub fn run(fs: FileSystem) {
|
pub fn run(fs: FileSystem) -> Result<()> {
|
||||||
let file_name = ".git-next.toml";
|
let pathbuf = fs.base().join(".git-next.toml");
|
||||||
let pathbuf = fs.base().join(file_name);
|
if fs
|
||||||
match fs.path_exists(&pathbuf) {
|
.path_exists(&pathbuf)
|
||||||
Ok(exists) => {
|
.with_context(|| format!("Checking for existing file: {pathbuf:?}"))?
|
||||||
if exists {
|
{
|
||||||
eprintln!(
|
eprintln!("The configuration file already exists at {pathbuf:?} - not overwritting it.",);
|
||||||
"The configuration file already exists at {} - not overwritting it.",
|
} else {
|
||||||
file_name
|
fs.file_write(&pathbuf, include_str!("../default.toml"))
|
||||||
);
|
.with_context(|| format!("Writing file: {pathbuf:?}"))?;
|
||||||
} else {
|
println!("Created a default configuration file at {pathbuf:?}");
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ use git_next_core::git;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use kxio::{fs, network::Network};
|
use kxio::{fs, network::Network};
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ enum Server {
|
||||||
Start,
|
Start,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<()> {
|
||||||
let fs = fs::new(PathBuf::default());
|
let fs = fs::new(PathBuf::default());
|
||||||
let net = Network::new_real();
|
let net = Network::new_real();
|
||||||
let repository_factory = git::repository::factory::real();
|
let repository_factory = git::repository::factory::real();
|
||||||
|
@ -42,16 +43,17 @@ fn main() {
|
||||||
|
|
||||||
match commands.command {
|
match commands.command {
|
||||||
Command::Init => {
|
Command::Init => {
|
||||||
init::run(fs);
|
init::run(fs)?;
|
||||||
}
|
}
|
||||||
Command::Server(server) => match server {
|
Command::Server(server) => match server {
|
||||||
Server::Init => {
|
Server::Init => {
|
||||||
server::init(fs);
|
server::init(fs)?;
|
||||||
}
|
}
|
||||||
Server::Start => {
|
Server::Start => {
|
||||||
let sleep_duration = std::time::Duration::from_secs(10);
|
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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,13 @@ impl Handler<FileUpdated> for ServerActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, _msg: FileUpdated, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _msg: FileUpdated, ctx: &mut Self::Context) -> Self::Result {
|
||||||
let server_config = match ServerConfig::load(&self.fs) {
|
match ServerConfig::load(&self.fs) {
|
||||||
Ok(server_config) => server_config,
|
Ok(server_config) => {
|
||||||
|
self.do_send(ReceiveServerConfig::new(server_config), ctx);
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!("Failed to load config file. Error: {}", err);
|
self.abort(ctx, format!("Failed to load config file. Error: {}", err));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.do_send(ReceiveServerConfig::new(server_config), ctx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,18 +12,15 @@ impl Handler<ReceiveServerConfig> for ServerActor {
|
||||||
fn handle(&mut self, msg: ReceiveServerConfig, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: ReceiveServerConfig, ctx: &mut Self::Context) -> Self::Result {
|
||||||
tracing::info!("recieved server config");
|
tracing::info!("recieved server config");
|
||||||
let Ok(socket_addr) = msg.http() else {
|
let Ok(socket_addr) = msg.http() else {
|
||||||
tracing::error!("Unable to parse http.addr");
|
self.abort(ctx, "Unable to parse http.addr");
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(server_storage) = self.server_storage(&msg) else {
|
let Some(server_storage) = self.server_storage(&msg) else {
|
||||||
tracing::error!("Server storage not available");
|
self.abort(ctx, "Server storage not available");
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if msg.inbound_webhook().base_url().ends_with('/') {
|
if msg.inbound_webhook().base_url().ends_with('/') {
|
||||||
tracing::error!("webhook.url must not end with a '/'");
|
self.abort(ctx, "webhook.url must not end with a '/'");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.do_send(
|
self.do_send(
|
||||||
|
|
|
@ -229,6 +229,19 @@ impl ServerActor {
|
||||||
Some(server_storage)
|
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)
|
fn do_send<M>(&mut self, msg: M, _ctx: &mut <Self as actix::Actor>::Context)
|
||||||
where
|
where
|
||||||
M: actix::Message + Send + 'static + std::fmt::Debug,
|
M: actix::Message + Send + 'static + std::fmt::Debug,
|
||||||
|
|
|
@ -10,31 +10,26 @@ use crate::file_watcher::{watch_file, FileUpdated};
|
||||||
use actor::ServerActor;
|
use actor::ServerActor;
|
||||||
use git_next_core::git::RepositoryFactory;
|
use git_next_core::git::RepositoryFactory;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
use kxio::{fs::FileSystem, network::Network};
|
use kxio::{fs::FileSystem, network::Network};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn init(fs: FileSystem) {
|
pub fn init(fs: FileSystem) -> Result<()> {
|
||||||
let file_name = "git-next-server.toml";
|
let file_name = "git-next-server.toml";
|
||||||
let pathbuf = PathBuf::from(file_name);
|
let pathbuf = PathBuf::from(file_name);
|
||||||
let Ok(exists) = fs.path_exists(&pathbuf) else {
|
if fs
|
||||||
eprintln!("Could not check if file exist: {}", file_name);
|
.path_exists(&pathbuf)
|
||||||
return;
|
.with_context(|| format!("Checking for existing file: {pathbuf:?}"))?
|
||||||
};
|
{
|
||||||
if exists {
|
eprintln!("The configuration file already exists at {pathbuf:?} - not overwritting it.",);
|
||||||
eprintln!(
|
|
||||||
"The configuration file already exists at {} - not overwritting it.",
|
|
||||||
file_name
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
match fs.file_write(&pathbuf, include_str!("server-default.toml")) {
|
fs.file_write(&pathbuf, include_str!("server-default.toml"))
|
||||||
Ok(_) => println!("Created a default configuration file at {}", file_name),
|
.with_context(|| format!("Writing file: {pathbuf:?}"))?;
|
||||||
Err(e) => {
|
println!("Created a default configuration file at {pathbuf:?}",);
|
||||||
eprintln!("Failed to write to the configuration file: {}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(
|
pub fn start(
|
||||||
|
@ -42,7 +37,7 @@ pub fn start(
|
||||||
net: Network,
|
net: Network,
|
||||||
repo: Box<dyn RepositoryFactory>,
|
repo: Box<dyn RepositoryFactory>,
|
||||||
sleep_duration: std::time::Duration,
|
sleep_duration: std::time::Duration,
|
||||||
) {
|
) -> Result<()> {
|
||||||
init_logging();
|
init_logging();
|
||||||
|
|
||||||
let execution = async move {
|
let execution = async move {
|
||||||
|
@ -51,7 +46,10 @@ pub fn start(
|
||||||
server.do_send(FileUpdated);
|
server.do_send(FileUpdated);
|
||||||
|
|
||||||
info!("Starting File Watcher...");
|
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...");
|
info!("Server running - Press Ctrl-C to stop...");
|
||||||
let _ = actix_rt::signal::ctrl_c().await;
|
let _ = actix_rt::signal::ctrl_c().await;
|
||||||
|
@ -62,9 +60,8 @@ pub fn start(
|
||||||
};
|
};
|
||||||
let system = System::new();
|
let system = System::new();
|
||||||
Arbiter::current().spawn(execution);
|
Arbiter::current().spawn(execution);
|
||||||
if let Err(err) = system.run() {
|
system.run()?;
|
||||||
tracing::error!(?err, "")
|
Ok(())
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_logging() {
|
pub fn init_logging() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ mod init {
|
||||||
let file = fs.base().join(".git-next.toml");
|
let file = fs.base().join(".git-next.toml");
|
||||||
fs.file_write(&file, "contents")?;
|
fs.file_write(&file, "contents")?;
|
||||||
|
|
||||||
crate::init::run(fs.clone());
|
crate::init::run(fs.clone())?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fs.file_read_to_string(&file)?,
|
fs.file_read_to_string(&file)?,
|
||||||
|
@ -23,7 +23,7 @@ mod init {
|
||||||
fn should_create_default_file_if_not_exists() -> TestResult {
|
fn should_create_default_file_if_not_exists() -> TestResult {
|
||||||
let fs = kxio::fs::temp()?;
|
let fs = kxio::fs::temp()?;
|
||||||
|
|
||||||
crate::init::run(fs.clone());
|
crate::init::run(fs.clone())?;
|
||||||
|
|
||||||
let file = fs.base().join(".git-next.toml");
|
let file = fs.base().join(".git-next.toml");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue