101 lines
3.4 KiB
Rust
101 lines
3.4 KiB
Rust
|
use anyhow::{bail, Context, Result};
|
||
|
use celery::{error::TaskError, task::TaskResult};
|
||
|
use diesel::prelude::*;
|
||
|
use std::{fs, path::Path};
|
||
|
use url::Url;
|
||
|
use uuidv7::Uuid;
|
||
|
|
||
|
use crate::{db, CONFIG};
|
||
|
|
||
|
fn get_repo_name(db_conn: &mut db::Connection, id: Uuid) -> Result<(String, String)> {
|
||
|
let (user_id, name) = db::schema::repositories::table
|
||
|
.select((
|
||
|
db::schema::repositories::user_id,
|
||
|
db::schema::repositories::name,
|
||
|
))
|
||
|
.filter(db::schema::repositories::id.eq(id))
|
||
|
.first::<(Uuid, String)>(db_conn)?;
|
||
|
let user_name = db::schema::users::table
|
||
|
.select(db::schema::users::name)
|
||
|
.filter(db::schema::users::id.eq(user_id))
|
||
|
.first::<String>(db_conn)?;
|
||
|
|
||
|
Ok((user_name, name))
|
||
|
}
|
||
|
|
||
|
fn repo_dir(user: &str, repo: &str) -> (String, String, String) {
|
||
|
let parent = format!("{}/{}", CONFIG.repos_dir, user);
|
||
|
let dir = format!("{}/{}", parent, repo);
|
||
|
|
||
|
(parent, dir, format!("{}/{}.git", user, repo))
|
||
|
}
|
||
|
|
||
|
fn do_task(parent_dir: &str, repo_dir: &str, full_name_path: &str) -> Result<()> {
|
||
|
let path = Path::new(repo_dir);
|
||
|
if path.exists() && path.is_dir() {
|
||
|
let repo = git2::Repository::open(repo_dir)?;
|
||
|
|
||
|
repo.find_remote("origin")?
|
||
|
.fetch(&[&CONFIG.gitea_pull_branch], None, None)?;
|
||
|
|
||
|
let fetch_head = repo.find_reference("FETCH_HEAD")?;
|
||
|
let fetch_commit = repo.reference_to_annotated_commit(&fetch_head)?;
|
||
|
let analysis = repo.merge_analysis(&[&fetch_commit])?;
|
||
|
|
||
|
if !analysis.0.is_up_to_date() {
|
||
|
if analysis.0.is_fast_forward() {
|
||
|
let refname = format!("refs/heads/{}", CONFIG.gitea_pull_branch);
|
||
|
let mut reference = repo.find_reference(&refname)?;
|
||
|
reference.set_target(fetch_commit.id(), "Fast-Forward")?;
|
||
|
repo.set_head(&refname)?;
|
||
|
repo.checkout_head(Some(git2::build::CheckoutBuilder::default().force()))?;
|
||
|
} else {
|
||
|
bail!("Fast-forward only!");
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
fs::create_dir_all(parent_dir)?;
|
||
|
|
||
|
let repo = git2::Repository::clone(
|
||
|
Url::parse(CONFIG.gitea_pull_url.as_str())?
|
||
|
.join(full_name_path)?
|
||
|
.as_str(),
|
||
|
repo_dir,
|
||
|
)?;
|
||
|
|
||
|
let (object, reference) =
|
||
|
repo.revparse_ext(&format!("remotes/origin/{}", CONFIG.gitea_pull_branch))?;
|
||
|
repo.checkout_tree(&object, None)?;
|
||
|
match reference {
|
||
|
Some(gref) => repo.set_head(gref.name().context("Could not get ref name")?),
|
||
|
None => repo.set_head_detached(object.id()),
|
||
|
}
|
||
|
.context("Failed to set HEAD")?;
|
||
|
};
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
#[celery::task]
|
||
|
pub async fn get_repo(id: Uuid) -> TaskResult<()> {
|
||
|
let db_conn = &mut match db::POOL.get() {
|
||
|
Ok(x) => x,
|
||
|
Err(e) => return Err(TaskError::UnexpectedError(format!("{:?}", e))),
|
||
|
};
|
||
|
|
||
|
let (user_name, repo_name) = match get_repo_name(db_conn, id) {
|
||
|
Ok(x) => x,
|
||
|
Err(e) => return Err(TaskError::UnexpectedError(format!("{:?}", e))),
|
||
|
};
|
||
|
|
||
|
let (parent_dir, repo_dir, full_name_path) = repo_dir(&user_name, &repo_name);
|
||
|
if let Err(e) = do_task(&parent_dir, &repo_dir, &full_name_path) {
|
||
|
if let Err(err) = fs::remove_dir_all(repo_dir) {
|
||
|
return Err(TaskError::UnexpectedError(format!("{:?}", err)));
|
||
|
}
|
||
|
return Err(TaskError::UnexpectedError(format!("{:?}", e)));
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|