gitea_pages/backend/src/worker/get_repo.rs

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(())
}