From 295a28bab4930e444d0b93b02bb0f7043aefb6ff Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Mon, 29 Mar 2021 20:52:04 +0200 Subject: [PATCH] lohr: add default and additional remotes Co-authored-by: Antoine Martin --- Cargo.lock | 34 ++++++++++++++++++++++++++ Cargo.toml | 1 + src/job.rs | 65 +++++++++++++++++++++++++++++++++++++------------ src/main.rs | 27 +++++++++++++++++--- src/settings.rs | 13 ++++++++++ 5 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 src/settings.rs diff --git a/Cargo.lock b/Cargo.lock index ce08ffb..8fcd247 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -231,6 +231,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + [[package]] name = "filetime" version = "0.2.14" @@ -459,6 +465,12 @@ version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + [[package]] name = "log" version = "0.3.9" @@ -486,6 +498,7 @@ dependencies = [ "rocket", "rocket_contrib", "serde", + "serde_yaml", ] [[package]] @@ -851,6 +864,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + [[package]] name = "sha2" version = "0.9.3" @@ -1104,6 +1129,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yansi" version = "0.5.0" diff --git a/Cargo.toml b/Cargo.toml index 14d5b4d..6aabd22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ log = "0.4.14" rocket = "0.4.7" rocket_contrib = { version = "0.4.7", features = [ "json" ] } serde = { version = "1.0.125", features = [ "derive" ] } +serde_yaml = "0.8.17" diff --git a/src/job.rs b/src/job.rs index 4142805..fd2f094 100644 --- a/src/job.rs +++ b/src/job.rs @@ -8,6 +8,7 @@ use anyhow::bail; use log::info; use crate::gitea::Repository; +use crate::settings::{GlobalSettings, RepoUrl}; pub(crate) struct Job { repo: Repository, @@ -91,7 +92,9 @@ impl Job { Ok(()) } - fn get_remotes(&self) -> anyhow::Result { + /// Can return Ok(None) if the .lohr file didn't exist, but no significant error occured + fn read_remotes_from_lohr_file(&self) -> anyhow::Result>> { + // try to read .lohr file from bare repo (hence the git show sorcery) let output = Command::new("git") .arg("-C") .arg(format!("{}", self.local_path.as_ref().unwrap().display())) @@ -101,24 +104,56 @@ impl Job { if !output.status.success() { let error = str::from_utf8(&output.stderr)?; - let code = output - .status - .code() - .unwrap_or_else(|| output.status.signal().unwrap()); - bail!( - "couldn't read .lohr file from repo {}: exit code {}, stderr:\n{}", - self.repo.full_name, - code, - error - ); + // this error case is okay, .lohr just doesn't exist + if error.contains("does not exist in 'HEAD'") { + return Ok(None); + } else { + let code = output + .status + .code() + .unwrap_or_else(|| output.status.signal().unwrap()); + + bail!( + "couldn't read .lohr file from repo {}: exit code {}, stderr:\n{}", + self.repo.full_name, + code, + error + ); + } } - Ok(String::from_utf8(output.stdout)?) + let output = String::from_utf8(output.stdout)?; + + Ok(Some(output.lines().map(String::from).collect())) } - fn update_mirrors(&self) -> anyhow::Result<()> { - for remote in self.get_remotes()?.lines() { + fn get_remotes(&self, config: &GlobalSettings) -> anyhow::Result> { + let local_path = self.local_path.as_ref().unwrap(); + + let stem_to_repo = |stem: &RepoUrl| -> RepoUrl { + let mut res = stem.clone(); + if !res.ends_with('/') { + res.push('/'); + }; + res.push_str(local_path.file_name().unwrap().to_str().unwrap()); + res + }; + + // use either .lohr file or default remotes from config + let mut remotes = match self.read_remotes_from_lohr_file()? { + Some(remotes) if !remotes.is_empty() => remotes, + _ => config.default_remotes.iter().map(stem_to_repo).collect(), + }; + + // additional remotes + remotes.append(&mut config.additional_remotes.iter().map(stem_to_repo).collect()); + + Ok(remotes) + } + + fn update_mirrors(&self, config: &GlobalSettings) -> anyhow::Result<()> { + for remote in &self.get_remotes(config)? { info!("Updating mirror {}:{}...", remote, self.repo.full_name); let output = Command::new("git") @@ -148,7 +183,7 @@ impl Job { Ok(()) } - pub(crate) fn run(&mut self, homedir: &Path) -> anyhow::Result<()> { + pub(crate) fn run(&mut self, homedir: &Path, config: &GlobalSettings) -> anyhow::Result<()> { let local_path = homedir.join(&self.repo.full_name); assert!(local_path.is_absolute()); self.local_path = Some(local_path); diff --git a/src/main.rs b/src/main.rs index b30da93..6bb3613 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![feature(proc_macro_hygiene, decl_macro)] use std::env; +use std::fs::File; use std::path::PathBuf; use std::sync::{ mpsc::{channel, Receiver, Sender}, @@ -19,6 +20,9 @@ use gitea::GiteaWebHook; mod job; use job::Job; +mod settings; +use settings::GlobalSettings; + struct JobSender(Mutex>); #[post("/", data = "")] @@ -34,29 +38,44 @@ fn gitea_webhook(payload: Json, sender: State) -> Statu Status::Ok } -fn repo_updater(rx: Receiver, homedir: PathBuf) { +fn repo_updater(rx: Receiver, homedir: PathBuf, config: GlobalSettings) { loop { let mut job = rx.recv().unwrap(); - if let Err(err) = job.run(&homedir) { + if let Err(err) = job.run(&homedir, &config) { error!("couldn't process job: {}", err); } } } -fn main() { +fn parse_config(mut path: PathBuf) -> anyhow::Result { + path.push("lohr-config"); + path.set_extension("yaml"); + let config = if let Ok(file) = File::open(path.as_path()) { + serde_yaml::from_reader(file)? + } else { + Default::default() + }; + Ok(config) +} + +fn main() -> anyhow::Result<()> { let (sender, receiver) = channel(); let homedir = env::var("LOHR_HOME").unwrap_or_else(|_| "./".to_string()); let homedir: PathBuf = homedir.into(); let homedir = homedir.canonicalize().expect("LOHR_HOME isn't valid!"); + let config = parse_config(homedir.clone())?; + thread::spawn(move || { - repo_updater(receiver, homedir); + repo_updater(receiver, homedir, config); }); rocket::ignite() .mount("/", routes![gitea_webhook]) .manage(JobSender(Mutex::new(sender))) .launch(); + + Ok(()) } diff --git a/src/settings.rs b/src/settings.rs new file mode 100644 index 0000000..bfe7744 --- /dev/null +++ b/src/settings.rs @@ -0,0 +1,13 @@ +use serde::Deserialize; + +pub(crate) type RepoUrl = String; // FIXME: probably needs a better type than this + +#[derive(Default, Deserialize)] +pub(crate) struct GlobalSettings { + /// List of remote stems to use when no `.lohr` file is found + #[serde(default)] + pub default_remotes: Vec, + /// List of remote stems to use for every repository + #[serde(default)] + pub additional_remotes: Vec, +}