summaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/git_root.rs265
-rw-r--r--server/src/main.rs3
-rw-r--r--server/src/trans.rs3
3 files changed, 194 insertions, 77 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 */ }
}
diff --git a/server/src/main.rs b/server/src/main.rs
index 8c59b23..e4dff68 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -1208,7 +1208,6 @@ async fn get_translation_review_users(
)]
#[post("/translation/<projectid>/new", data = "<data>")]
async fn translation_review_new(
- db: &Db,
mut conn: Connection<Db>,
roots_state: &State<git_root::Roots>,
session: auth::Session,
@@ -1259,7 +1258,7 @@ async fn translation_review_new(
}
roots_state
- .new_translation_review(db, projectid, translation_reviewid, base.as_str())
+ .new_translation_review(&mut conn, projectid, translation_reviewid, base.as_str())
.map_err(|e| Custom(Status::InternalServerError, format!("{e}")))
.await?;
diff --git a/server/src/trans.rs b/server/src/trans.rs
index c4e3b45..3f47c8b 100644
--- a/server/src/trans.rs
+++ b/server/src/trans.rs
@@ -4,6 +4,7 @@ use anyhow;
use futures::stream::TryStreamExt;
use rocket_db_pools::{sqlx, Database, Pool};
use sorted_insert::SortedInsertByKey;
+use sqlx::Acquire;
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::fs::File;
@@ -262,7 +263,7 @@ struct TranslationString {
}
pub async fn review_add_strings(
- db: &Db,
+ db: &mut DbConnection,
translation_reviewid: u64,
strings: Vec<api_model::LocalizationString>,
base: bool,