first working POC
This commit is contained in:
parent
b37891af49
commit
2bd817099f
|
@ -1,13 +1,13 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, Deserialize)]
|
||||||
pub(crate) struct Repository {
|
pub(crate) struct Repository {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) full_name: String,
|
pub(crate) full_name: String,
|
||||||
pub(crate) clone_url: String,
|
pub(crate) clone_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize)]
|
||||||
pub(crate) struct GiteaWebHook {
|
pub(crate) struct GiteaWebHook {
|
||||||
pub(crate) repository: Repository,
|
pub(crate) repository: Repository,
|
||||||
}
|
}
|
||||||
|
|
142
src/job.rs
142
src/job.rs
|
@ -1,12 +1,140 @@
|
||||||
pub struct Job {
|
use std::os::unix::process::ExitStatusExt;
|
||||||
repo: String,
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::Command;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::gitea::Repository;
|
||||||
|
|
||||||
|
pub(crate) struct Job {
|
||||||
|
repo: Repository,
|
||||||
|
local_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement git operations with git2-rs where possible
|
||||||
|
|
||||||
impl Job {
|
impl Job {
|
||||||
pub fn new(repo: String) -> Self {
|
const REMOTES: &'static [&'static str] = &["github", "gitlab"];
|
||||||
Self { repo }
|
|
||||||
}
|
pub(crate) fn new(repo: Repository) -> Self {
|
||||||
pub fn run(&self) -> anyhow::Result<()> {
|
Self {
|
||||||
todo!()
|
repo,
|
||||||
|
local_path: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn repo_exists(&self) -> bool {
|
||||||
|
self.local_path
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.is_dir())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mirror_repo(&self) -> anyhow::Result<()> {
|
||||||
|
info!("Cloning repo {}...", self.repo.full_name);
|
||||||
|
|
||||||
|
let output = Command::new("git")
|
||||||
|
.arg("clone")
|
||||||
|
.arg("--mirror")
|
||||||
|
.arg(&self.repo.clone_url)
|
||||||
|
.arg(format!("{}", self.local_path.as_ref().unwrap().display()))
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
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 mirror repo: exit code {}, stderr:\n{}",
|
||||||
|
code,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle git LFS mirroring:
|
||||||
|
// https://github.com/git-lfs/git-lfs/issues/2342#issuecomment-310323647
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_repo(&self) -> anyhow::Result<()> {
|
||||||
|
info!("Updating repo {}...", self.repo.full_name);
|
||||||
|
|
||||||
|
let output = Command::new("git")
|
||||||
|
.arg("-C")
|
||||||
|
.arg(format!("{}", self.local_path.as_ref().unwrap().display()))
|
||||||
|
.arg("remote")
|
||||||
|
.arg("update")
|
||||||
|
.arg("origin")
|
||||||
|
// otherwise deleted tags and branches aren't updated on local copy
|
||||||
|
.arg("--prune")
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
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 update origin remote: exit code {}, stderr:\n{}",
|
||||||
|
code,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_mirrors(&self) -> anyhow::Result<()> {
|
||||||
|
for remote in Self::REMOTES.iter() {
|
||||||
|
info!("Updating mirror {}:{}...", remote, self.repo.full_name);
|
||||||
|
|
||||||
|
let output = Command::new("git")
|
||||||
|
.arg("-C")
|
||||||
|
.arg(format!("{}", self.local_path.as_ref().unwrap().display()))
|
||||||
|
.arg("push")
|
||||||
|
.arg("--mirror")
|
||||||
|
.arg(format!("git@{}.com:{}", remote, self.repo.full_name))
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
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 update origin remote: exit code {}, stderr:\n{}",
|
||||||
|
code,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn run(&mut self, homedir: &Path) -> anyhow::Result<()> {
|
||||||
|
let local_path = homedir.join(&self.repo.full_name);
|
||||||
|
println!("{}", local_path.display());
|
||||||
|
assert!(local_path.is_absolute());
|
||||||
|
self.local_path = Some(local_path);
|
||||||
|
|
||||||
|
if !self.repo_exists() {
|
||||||
|
self.mirror_repo()?;
|
||||||
|
} else {
|
||||||
|
self.update_repo()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_mirrors()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -1,13 +1,14 @@
|
||||||
#![feature(proc_macro_hygiene, decl_macro)]
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::env;
|
||||||
|
use std::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 rocket::{fairing::AdHoc, http::Status, post, routes, State};
|
use rocket::{http::Status, post, routes, State};
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
|
|
||||||
use log::error;
|
use log::error;
|
||||||
|
@ -18,26 +19,24 @@ use gitea::GiteaWebHook;
|
||||||
mod job;
|
mod job;
|
||||||
use job::Job;
|
use job::Job;
|
||||||
|
|
||||||
struct HomeDir(PathBuf);
|
|
||||||
struct JobSender(Mutex<Sender<Job>>);
|
struct JobSender(Mutex<Sender<Job>>);
|
||||||
|
|
||||||
#[post("/", data = "<payload>")]
|
#[post("/", data = "<payload>")]
|
||||||
fn gitea_webhook(payload: Json<GiteaWebHook>, sender: State<JobSender>) -> Status {
|
fn gitea_webhook(payload: Json<GiteaWebHook>, sender: State<JobSender>) -> Status {
|
||||||
{
|
{
|
||||||
let sender = sender.0.lock().unwrap();
|
let sender = sender.0.lock().unwrap();
|
||||||
sender
|
let repo = &payload.repository;
|
||||||
.send(Job::new(payload.repository.full_name.clone()))
|
sender.send(Job::new(repo.clone())).unwrap();
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Status::Ok
|
Status::Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
fn repo_updater(rx: Receiver<Job>) {
|
fn repo_updater(rx: Receiver<Job>, homedir: PathBuf) {
|
||||||
loop {
|
loop {
|
||||||
let job = rx.recv().unwrap();
|
let mut job = rx.recv().unwrap();
|
||||||
|
|
||||||
if let Err(err) = job.run() {
|
if let Err(err) = job.run(&homedir) {
|
||||||
error!("couldn't process job: {}", err);
|
error!("couldn't process job: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,19 +45,16 @@ fn repo_updater(rx: Receiver<Job>) {
|
||||||
fn main() {
|
fn main() {
|
||||||
let (sender, receiver) = channel();
|
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!");
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
repo_updater(receiver);
|
repo_updater(receiver, homedir);
|
||||||
});
|
});
|
||||||
|
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.mount("/", routes![gitea_webhook])
|
.mount("/", routes![gitea_webhook])
|
||||||
.manage(JobSender(Mutex::new(sender)))
|
.manage(JobSender(Mutex::new(sender)))
|
||||||
.attach(AdHoc::on_attach("Assets Config", |rocket| {
|
|
||||||
let home_dir = rocket.config().get_str("home").unwrap();
|
|
||||||
|
|
||||||
let home_dir = Path::new(home_dir).into();
|
|
||||||
|
|
||||||
Ok(rocket.manage(HomeDir(home_dir)))
|
|
||||||
}))
|
|
||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue