diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2025-01-26 23:55:50 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2025-01-26 23:55:50 +0100 |
| commit | d1647b7a056f04ad5828976dd5a7e2e06b431feb (patch) | |
| tree | 11a4e1ad3f40bf653b7971bd40e54568f507ffb5 /server/src/git.rs | |
| parent | 4fe3e610d5697aca4dfdc1033261130ec2b431ee (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.rs | 64 |
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))] |
