summaryrefslogtreecommitdiff
path: root/server/src/git.rs
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2025-01-26 23:55:50 +0100
committerJoel Klinghed <the_jk@spawned.biz>2025-01-26 23:55:50 +0100
commitd1647b7a056f04ad5828976dd5a7e2e06b431feb (patch)
tree11a4e1ad3f40bf653b7971bd40e54568f507ffb5 /server/src/git.rs
parent4fe3e610d5697aca4dfdc1033261130ec2b431ee (diff)
Stop using current user in git hooks
Want to support any authentication for the git server, so use git commiter as username for creating reviews instead of the local user that logged in to git. Also verify that pushed commits has a valid author in pre-receive. This is tricky as pre-receive must do this check in the hook, because pre-receive runs when before the objects are pushed so the server can't read the commits, the hook must do this.
Diffstat (limited to 'server/src/git.rs')
-rw-r--r--server/src/git.rs64
1 files changed, 64 insertions, 0 deletions
diff --git a/server/src/git.rs b/server/src/git.rs
index 9fb0593..7add644 100644
--- a/server/src/git.rs
+++ b/server/src/git.rs
@@ -31,6 +31,8 @@ impl fmt::Display for Error {
impl std::error::Error for Error {}
+pub const EMPTY: &str = "0000000000000000000000000000000000000000";
+
struct RepoData {
// Only one fetch at a time, and they should be in queue
fetch_semaphore: Semaphore,
@@ -50,10 +52,30 @@ pub struct Repository {
lock: RwLock<RepoData>,
}
+#[allow(dead_code)]
+pub struct User {
+ pub name: String,
+ pub email: String,
+ // Part before '@' in email
+ pub username: String,
+}
+
fn io_err(action: &str, e: std::io::Error) -> Error {
Error::new(format!("{action}: {e}"))
}
+fn parse_user(output: String) -> User {
+ let mut lines = output.lines();
+ let name = lines.next().unwrap_or("").to_string();
+ let username = lines.next().unwrap_or("").to_string();
+ let email = lines.next().unwrap_or("").to_string();
+ User {
+ name,
+ email,
+ username,
+ }
+}
+
impl RepoData {
fn new() -> Self {
Self {
@@ -246,6 +268,34 @@ impl RepoData {
self.check(&mut cmd).await
}
+ async fn get_author(&self, repo: &Repository, commit: &str) -> Result<User, Error> {
+ self.get_log_format(repo, commit, "%an%n%al%n%ae")
+ .map_ok(|output| parse_user(output))
+ .await
+ }
+
+ async fn get_commiter(&self, repo: &Repository, commit: &str) -> Result<User, Error> {
+ self.get_log_format(repo, commit, "%cn%n%cl%n%ce")
+ .map_ok(|output| parse_user(output))
+ .await
+ }
+
+ async fn get_log_format(
+ &self,
+ repo: &Repository,
+ commit: &str,
+ format: &str,
+ ) -> Result<String, Error> {
+ let mut cmd = self.git_cmd(repo);
+ cmd.arg("log")
+ .arg("-1")
+ .arg("--no-decorate")
+ .arg("--no-mailmap")
+ .arg(format!("--pretty=format:{format}"))
+ .arg(commit);
+ self.output(&mut cmd).await
+ }
+
fn git_cmd(&self, repo: &Repository) -> Command {
let mut cmd = Command::new("git");
// Run as if git was started in <path> instead of the current working directory.
@@ -445,6 +495,20 @@ impl Repository {
data.is_equal_content(self, commit1.as_str(), commit2.as_str())
.await
}
+
+ pub async fn get_author(&self, commit: impl Into<String>) -> Result<User, Error> {
+ let commit = commit.into();
+ let data = self.lock.read().await;
+
+ data.get_author(self, commit.as_str()).await
+ }
+
+ pub async fn get_commiter(&self, commit: impl Into<String>) -> Result<User, Error> {
+ let commit = commit.into();
+ let data = self.lock.read().await;
+
+ data.get_commiter(self, commit.as_str()).await
+ }
}
#[cfg(not(test))]