Compare commits

..

9 commits

6 changed files with 183 additions and 21 deletions

100
Cargo.lock generated
View file

@ -56,6 +56,24 @@ dependencies = [
"opaque-debug", "opaque-debug",
] ]
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.40" version = "1.0.40"
@ -143,6 +161,21 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.11.4" version = "0.11.4"
@ -467,9 +500,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.91" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
@ -500,13 +533,16 @@ name = "lohr"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap",
"hex", "hex",
"hmac", "hmac",
"log 0.4.14", "log 0.4.14",
"regex",
"rocket", "rocket",
"rocket_contrib", "rocket_contrib",
"serde", "serde",
"serde_json", "serde_json",
"serde_regex",
"serde_yaml", "serde_yaml",
"sha2", "sha2",
] ]
@ -756,6 +792,23 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "regex"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
[[package]] [[package]]
name = "rocket" name = "rocket"
version = "0.4.7" version = "0.4.7"
@ -860,7 +913,7 @@ checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [ dependencies = [
"proc-macro2 1.0.24", "proc-macro2 1.0.24",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.65", "syn 1.0.67",
] ]
[[package]] [[package]]
@ -874,6 +927,16 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_regex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
dependencies = [
"regex",
"serde",
]
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.8.17" version = "0.8.17"
@ -917,6 +980,12 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3015a7d0a5fd5105c91c3710d42f9ccf0abfb287d62206484dcc67f9569a6483" checksum = "3015a7d0a5fd5105c91c3710d42f9ccf0abfb287d62206484dcc67f9569a6483"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.4.0" version = "2.4.0"
@ -936,15 +1005,24 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.65" version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702"
dependencies = [ dependencies = [
"proc-macro2 1.0.24", "proc-macro2 1.0.24",
"quote 1.0.9", "quote 1.0.9",
"unicode-xid 0.2.1", "unicode-xid 0.2.1",
] ]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.43" version = "0.1.43"
@ -1024,6 +1102,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.1.0" version = "0.1.0"
@ -1057,6 +1141,12 @@ dependencies = [
"percent-encoding 1.0.1", "percent-encoding 1.0.1",
] ]
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.1.5" version = "0.1.5"

View file

@ -12,12 +12,15 @@ repository = "https://github.com/alarsyo/lohr"
[dependencies] [dependencies]
anyhow = "1.0.40" anyhow = "1.0.40"
clap = "2.33.3"
hex = "0.4.3" hex = "0.4.3"
hmac = "0.10.1" hmac = "0.10.1"
log = "0.4.14" log = "0.4.14"
regex = "1"
rocket = "0.4.7" rocket = "0.4.7"
rocket_contrib = { version = "0.4.7", features = [ "json" ] } rocket_contrib = { version = "0.4.7", features = [ "json" ] }
serde = { version = "1.0.125", features = [ "derive" ] } serde = { version = "1.0.125", features = [ "derive" ] }
serde_json = "1.0.64" serde_json = "1.0.64"
serde_regex = "1.1.0"
serde_yaml = "0.8.17" serde_yaml = "0.8.17"
sha2 = "0.9.3" sha2 = "0.9.3"

View file

@ -73,8 +73,13 @@ variable.
**** Extra remote configuration **** Extra remote configuration
=lohr= looks for a =lohr-config.yaml= file in its =LOHR_HOME= directory. This You can provide =lohr= with a YAML file containing additional configuration. You
file takes the following format: can pass its path to the =--config= flag when launching =lohr=. If no
configuration is provided via a CLI flag, =lohr= will check the =LOHR_CONFIG=
environment variable. If the environment variable isn't set either, it will
check in =LOHR_HOME= is a =lohr-config.yaml= file exists, and try to load it.
This file takes the following format:
#+begin_src yaml #+begin_src yaml
default_remotes: default_remotes:
@ -83,12 +88,18 @@ default_remotes:
additional_remotes: additional_remotes:
- "git@git.sr.ht:~user" - "git@git.sr.ht:~user"
blacklist:
- "private-.*"
#+end_src #+end_src
- ~default_remotes~ is a list of remotes to use if no ~.lohr~ file is found in a - ~default_remotes~ is a list of remotes to use if no ~.lohr~ file is found in a
repository. repository.
- ~additional_remotes~ is a list of remotes to add in any case, whether the - ~additional_remotes~ is a list of remotes to add in any case, whether the
original set of remotes is set via ~default_remotes~ or via a =.lohr= file. original set of remotes is set via ~default_remotes~ or via a =.lohr= file.
- ~blacklist~ is a list of regular expressions to match against the full
repository names. Any that matches will not be mirrored, even if it contains a
`.lohr` file.
Both settings take as input a list of "stems", i.e. incomplete remote addresses, Both settings take as input a list of "stems", i.e. incomplete remote addresses,
to which the repo's name will be appended (so for example, if my to which the repo's name will be appended (so for example, if my

View file

@ -37,6 +37,13 @@
defaultPackage = naersk-lib.buildPackage { defaultPackage = naersk-lib.buildPackage {
src = ./.; src = ./.;
pname = "lohr"; pname = "lohr";
meta = with pkgs.lib; {
description = "A Git mirroring tool";
homepage = "https://github.com/alarsyo/lohr";
license = with licenses; [ mit asl20 ];
platforms = platforms.unix;
};
}; };
defaultApp = flake-utils.lib.mkApp { defaultApp = flake-utils.lib.mkApp {

View file

@ -2,17 +2,18 @@
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::sync::{ use std::sync::{
mpsc::{channel, Receiver, Sender}, mpsc::{channel, Receiver, Sender},
Mutex, Mutex,
}; };
use std::thread; use std::thread;
use anyhow::Context;
use clap::{App, Arg};
use log::{error, info};
use rocket::{http::Status, post, routes, State}; use rocket::{http::Status, post, routes, State};
use log::error;
mod gitea; mod gitea;
use gitea::GiteaWebHook; use gitea::GiteaWebHook;
@ -29,7 +30,23 @@ struct JobSender(Mutex<Sender<Job>>);
struct Secret(String); struct Secret(String);
#[post("/", data = "<payload>")] #[post("/", data = "<payload>")]
fn gitea_webhook(payload: SignedJson<GiteaWebHook>, sender: State<JobSender>) -> Status { fn gitea_webhook(
payload: SignedJson<GiteaWebHook>,
sender: State<JobSender>,
config: State<GlobalSettings>,
) -> Status {
if config
.blacklist
.iter()
.any(|re| re.is_match(&payload.repository.full_name))
{
info!(
"Ignoring webhook for repo {} which is blacklisted",
payload.repository.full_name
);
return Status::Ok;
}
{ {
let sender = sender.0.lock().unwrap(); let sender = sender.0.lock().unwrap();
let repo = &payload.repository; let repo = &payload.repository;
@ -49,18 +66,46 @@ fn repo_updater(rx: Receiver<Job>, homedir: PathBuf, config: GlobalSettings) {
} }
} }
fn parse_config(mut path: PathBuf) -> anyhow::Result<GlobalSettings> { fn parse_config(home: &Path, flags: &clap::ArgMatches) -> anyhow::Result<GlobalSettings> {
path.push("lohr-config"); // prioritize CLI flag, then env var
path.set_extension("yaml"); let config_path = flags.value_of("config").map(PathBuf::from);
let config = if let Ok(file) = File::open(path.as_path()) { let config_path = config_path.or_else(|| env::var("LOHR_CONFIG").map(PathBuf::from).ok());
serde_yaml::from_reader(file)?
} else { let file = match config_path {
Default::default() Some(config_path) => File::open(&config_path).with_context(|| {
format!(
"could not open provided configuration file at {}",
config_path.display()
)
})?,
None => {
// check if file exists in lohr home
let config_path = home.join("lohr-config.yaml");
if !config_path.is_file() {
return Ok(Default::default());
}
File::open(config_path).context("failed to open configuration file in LOHR_HOME")?
}
}; };
Ok(config)
serde_yaml::from_reader(file).context("could not parse configuration file")
} }
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let matches = App::new("lohr")
.version("0.3.0")
.about("Git mirroring daemon")
.arg(
Arg::with_name("config")
.short("c")
.long("config")
.value_name("FILE")
.help("Use a custom config file")
.takes_value(true),
)
.get_matches();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
let homedir = env::var("LOHR_HOME").unwrap_or_else(|_| "./".to_string()); let homedir = env::var("LOHR_HOME").unwrap_or_else(|_| "./".to_string());
@ -70,7 +115,8 @@ fn main() -> anyhow::Result<()> {
let secret = env::var("LOHR_SECRET") let secret = env::var("LOHR_SECRET")
.expect("please provide a secret, otherwise anyone can send you a malicious webhook"); .expect("please provide a secret, otherwise anyone can send you a malicious webhook");
let config = parse_config(homedir.clone())?; let config = parse_config(&homedir, &matches)?;
let config_state = config.clone();
thread::spawn(move || { thread::spawn(move || {
repo_updater(receiver, homedir, config); repo_updater(receiver, homedir, config);
@ -80,6 +126,7 @@ fn main() -> anyhow::Result<()> {
.mount("/", routes![gitea_webhook]) .mount("/", routes![gitea_webhook])
.manage(JobSender(Mutex::new(sender))) .manage(JobSender(Mutex::new(sender)))
.manage(Secret(secret)) .manage(Secret(secret))
.manage(config_state)
.launch(); .launch();
Ok(()) Ok(())

View file

@ -2,7 +2,7 @@ use serde::Deserialize;
pub(crate) type RepoUrl = String; // FIXME: probably needs a better type than this pub(crate) type RepoUrl = String; // FIXME: probably needs a better type than this
#[derive(Default, Deserialize)] #[derive(Clone, Default, Deserialize)]
pub(crate) struct GlobalSettings { pub(crate) struct GlobalSettings {
/// List of remote stems to use when no `.lohr` file is found /// List of remote stems to use when no `.lohr` file is found
#[serde(default)] #[serde(default)]
@ -10,4 +10,8 @@ pub(crate) struct GlobalSettings {
/// List of remote stems to use for every repository /// List of remote stems to use for every repository
#[serde(default)] #[serde(default)]
pub additional_remotes: Vec<RepoUrl>, pub additional_remotes: Vec<RepoUrl>,
/// List of regexes, if a repository's name matches any of the, it is not mirrored by `lohr`
/// even if it contains a `.lorh` file.
#[serde(with = "serde_regex")]
pub blacklist: Vec<regex::Regex>,
} }