diff options
Diffstat (limited to 'server/src/git_root.rs')
| -rw-r--r-- | server/src/git_root.rs | 265 |
1 files changed, 191 insertions, 74 deletions
diff --git a/server/src/git_root.rs b/server/src/git_root.rs index f68ed92..ec741c4 100644 --- a/server/src/git_root.rs +++ b/server/src/git_root.rs @@ -74,7 +74,7 @@ impl Roots { pub async fn new_translation_review( &self, - db: &Db, + db: &mut DbConnection, project_id: &str, translation_reviewid: u64, base: &str, @@ -89,38 +89,7 @@ impl Roots { } } - let mut entries = repo - .ls_tree(base, true) - .await - .map_err(|e| anyhow::Error::new(e))?; - entries.retain(|e| e.object_type == git::ObjectType::BLOB); - let grits = entries - .iter() - .filter(|x| x.path.ends_with(".grd")) - .map(|x| x.path.to_string()) - .collect::<Vec<String>>(); - let entries = Arc::new(entries); - let strings = trans::collect_strings_with_opener(grits, move |path| { - for entry in &*entries { - if entry.path == path { - let rt = tokio::runtime::Handle::current(); - let object_name = entry.object_name.clone(); - let repo = repo.clone(); - return rt.block_on(async move { - repo.cat_file(git::ObjectType::BLOB, object_name) - .await - .map(|x| BufReader::new(x)) - .map_err(|e| anyhow::Error::new(e)) - }); - } - } - Err(anyhow::Error::msg(format!("No such file: {path}"))) - }) - .await?; - - trans::review_add_strings(db, translation_reviewid, strings, true).await?; - - Ok(()) + trans_review_add_strings(db, repo, translation_reviewid, base, true).await } pub async fn fetch_branch(&self, project_id: &str, branch: &str) -> anyhow::Result<String> { @@ -181,10 +150,52 @@ fn valid_branch_name(name: &str) -> bool { name.starts_with("refs/heads/") && is_printable(name) } +async fn trans_review_add_strings( + db: &mut DbConnection, + repo: Arc<git::Repository>, + translation_reviewid: u64, + commit: &str, + base: bool, +) -> Result<(), anyhow::Error> { + let mut entries = repo + .ls_tree(commit, true) + .await + .map_err(|e| anyhow::Error::new(e))?; + entries.retain(|e| e.object_type == git::ObjectType::BLOB); + let grits = entries + .iter() + .filter(|x| x.path.ends_with(".grd")) + .map(|x| x.path.to_string()) + .collect::<Vec<String>>(); + let entries = Arc::new(entries); + let strings = trans::collect_strings_with_opener(grits, move |path| { + for entry in &*entries { + if entry.path == path { + let rt = tokio::runtime::Handle::current(); + let object_name = entry.object_name.clone(); + let repo = repo.clone(); + return rt.block_on(async move { + repo.cat_file(git::ObjectType::BLOB, object_name) + .await + .map(|x| BufReader::new(x)) + .map_err(|e| anyhow::Error::new(e)) + }); + } + } + Err(anyhow::Error::msg(format!("No such file: {path}"))) + }) + .await?; + + trans::review_add_strings(db, translation_reviewid, strings, base).await?; + + Ok(()) +} + async fn git_process_prehook( - repo: &git::Repository, + repo: Arc<git::Repository>, mut db: DbConnection, receive: &Vec<git_socket::GitReceive>, + env: &git::Env, ) -> Result<git_socket::GitHookResponse, IoError> { let mut errors: Vec<String> = Vec::new(); @@ -213,13 +224,15 @@ async fn git_process_prehook( let branch = row.reference.strip_prefix("refs/heads/").unwrap(); if row.new_value != git::EMPTY { - match row.commiter { - Some(ref commiter) => match sqlx::query!( + match repo.get_commiter_with_env(&row.new_value, env).await { + Ok(commiter) => match sqlx::query!( "SELECT id FROM users WHERE id=? AND dn IS NOT NULL", - commiter, + commiter.username, ) .fetch_one(&mut *db) - .map_err(|_| IoError::new(format!("{branch}: Unknown commiter {}", commiter))) + .map_err(|_| { + IoError::new(format!("{branch}: Unknown commiter {}", commiter.username)) + }) .await { Ok(_) => {} @@ -229,7 +242,7 @@ async fn git_process_prehook( continue; } }, - None => { + Err(_) => { error!("{branch}: Missing commiter"); errors.push(format!("{branch}: Missing commiter")); continue; @@ -237,31 +250,67 @@ async fn git_process_prehook( } } - if row.old_value == git::EMPTY { + let translation_review_id; + + if branch.starts_with("t/") { + // Translation review + + translation_review_id = branch[2..].parse::<u64>().ok(); + if translation_review_id.is_none() { + error!("{branch}: Invalid translation review branch"); + errors.push(format!("{branch}: Invalid translation review branch")); + } + } else { + translation_review_id = None; + } + + if row.old_value == git::EMPTY && translation_review_id.is_none() { // Creating new review, nothing to check (yet). continue; } - let result = sqlx::query!( - "SELECT state, rewrite FROM reviews WHERE project=? AND branch=?", - repo.project_id().unwrap_or(""), - branch - ) - .fetch_one(&mut *db) - .map_ok(|r| { - ( - api_model::ReviewState::try_from(r.state).unwrap(), - api_model::Rewrite::try_from(r.rewrite).unwrap(), - ) - }) - .map_err(|_| IoError::new(format!("{branch}: Unknown branch"))) - .await; + let result = match translation_review_id { + None => { + sqlx::query!( + "SELECT state, rewrite FROM reviews WHERE project=? AND branch=?", + repo.project_id().unwrap_or(""), + branch + ) + .fetch_one(&mut *db) + .map_ok(|r| { + ( + api_model::ReviewState::try_from(r.state).unwrap(), + api_model::Rewrite::try_from(r.rewrite).unwrap(), + "".to_string(), + ) + }) + .map_err(|_| IoError::new(format!("{branch}: Unknown branch"))) + .await + } + Some(id) => { + sqlx::query!( + "SELECT state, base FROM translation_reviews WHERE project=? AND id=?", + repo.project_id().unwrap_or(""), + id, + ) + .fetch_one(&mut *db) + .map_ok(|r| { + ( + api_model::ReviewState::try_from(r.state).unwrap(), + api_model::Rewrite::Disabled, + r.base, + ) + }) + .map_err(|_| IoError::new(format!("{branch}: No such translation review"))) + .await + } + }; if row.new_value == git::EMPTY { // Do not allow to delete branch if there is a review connected to the branch. // All branches should be connected to a branch, but in case of errors this might // be relevant. - if result.is_ok() { + if result.is_ok() || translation_review_id.is_some() { error!("{branch}: Not allowed to delete branch, delete review instead."); errors.push(format!( "{branch}: Not allowed to delete branch, delete review instead." @@ -270,7 +319,7 @@ async fn git_process_prehook( continue; } - let (state, rewrite) = match result { + let (state, rewrite, base) = match result { Ok(data) => data, Err(e) => { error!("{e:?}"); @@ -294,11 +343,25 @@ async fn git_process_prehook( api_model::ReviewState::Open => {} } + let history_origin = if translation_review_id.is_some() { + &base + } else { + &row.old_value + }; + // Check for fast forward, if so we can skip the rest of the checks. - let is_fast_forward = repo - .is_ancestor(&row.old_value, &row.new_value) + let is_fast_forward = match repo + .is_ancestor_with_env(history_origin, &row.new_value, env) .map_err(|e| IoError::new(format!("{branch}: {}", e))) - .await?; + .await + { + Ok(ret) => ret, + Err(e) => { + error!("{e:?}"); + errors.push(e.message); + continue; + } + }; if is_fast_forward { continue; } @@ -317,11 +380,18 @@ async fn git_process_prehook( } api_model::Rewrite::Rebase => {} api_model::Rewrite::Disabled => { - error!("{}: Non fast-forward not allowed", row.reference); - errors.push(format!( - "Non fast-forward not allowed for review: {}", - row.reference - )); + if translation_review_id.is_none() { + error!("{}: Non fast-forward not allowed", row.reference); + errors.push(format!( + "Non fast-forward not allowed for review: {}", + row.reference + )); + } else { + error!("{branch}: Must be based on translation review base {base}"); + errors.push(format!( + "{branch}: Must be based on translation review base {base}" + )); + } } } } @@ -340,12 +410,13 @@ async fn git_process_prehook( } async fn git_process_posthook( - repo: &git::Repository, + repo: Arc<git::Repository>, mut db: DbConnection, receive: &Vec<git_socket::GitReceive>, ) -> git_socket::GitHookResponse { let mut messages: Vec<String> = Vec::new(); - let mut updated: Vec<u64> = Vec::new(); + let mut updated_reviews: Vec<u64> = Vec::new(); + let mut updated_translation_reviews: Vec<u64> = Vec::new(); for row in receive { trace!( @@ -357,6 +428,49 @@ async fn git_process_posthook( let branch = row.reference.strip_prefix("refs/heads/").unwrap(); + if branch.starts_with("t/") { + // pre-hook already checked format + let id = branch[2..].parse::<u64>().unwrap(); + + match sqlx::query!( + "UPDATE translation_reviews SET head=? WHERE project=? AND id=?", + row.new_value, + repo.project_id(), + id, + ) + .execute(&mut *db) + .map_err(|e| IoError::new(format!("Database error: {e:?}"))) + .await + { + Ok(_) => { + match trans_review_add_strings( + &mut db, + repo.clone(), + id, + row.new_value.as_str(), + false, + ) + .await + { + Ok(_) => { + updated_translation_reviews.push(id); + messages.push(format!("{branch}: Translation review updated")); + } + Err(e) => { + error!("{e:?}"); + messages.push(format!("{branch}: Error {e}",)); + } + } + } + Err(e) => { + error!("{e:?}"); + messages.push(format!("{branch}: Error {e}",)); + } + } + + continue; + } + if row.old_value == git::EMPTY { let commiter = match repo.get_commiter(row.reference.as_str()).await { Ok(user) => user, @@ -380,7 +494,7 @@ async fn git_process_posthook( .await { Ok(result) => { - updated.push(result.last_insert_id()); + updated_reviews.push(result.last_insert_id()); messages.push(format!( "{branch}: Review draft created, finalize at {}", "TODO" @@ -416,7 +530,7 @@ async fn git_process_posthook( "{branch}: Review draft created, finalize at {}", "TODO" )); - updated.push(id); + updated_reviews.push(id); } Err(e) => { error!("{e:?}"); @@ -425,7 +539,7 @@ async fn git_process_posthook( } } api_model::Rewrite::Disabled => { - updated.push(id); + updated_reviews.push(id); } }, Err(e) => { @@ -443,7 +557,7 @@ async fn git_process_posthook( } async fn git_socket_process( - repo: &git::Repository, + repo: Arc<git::Repository>, db: DbConnection, stream: UnixStream, ) -> Result<(), IoError> { @@ -460,8 +574,13 @@ async fn git_socket_process( .map_err(|e| IoError::new(e.to_string())) .await??; + let env = git::Env { + object_dir: request.object_dir, + alt_object_dirs: request.alt_object_dirs, + }; + let response = if request.pre { - git_process_prehook(repo, db, &request.receive).await? + git_process_prehook(repo, db, &request.receive, &env).await? } else { git_process_posthook(repo, db, &request.receive).await }; @@ -492,9 +611,7 @@ async fn git_socket_listen( match db.get().await { Ok(conn) => { let repo2 = repo.clone(); - tokio::spawn(async move { - git_socket_process(repo2.as_ref(), conn, stream).await - }); + tokio::spawn(async move { git_socket_process(repo2, conn, stream).await }); } Err(_) => { /* unable to access db */ } } |
