diff options
Diffstat (limited to 'server/hook/src')
| -rw-r--r-- | server/hook/src/githook.rs | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/server/hook/src/githook.rs b/server/hook/src/githook.rs new file mode 100644 index 0000000..a9cb898 --- /dev/null +++ b/server/hook/src/githook.rs @@ -0,0 +1,105 @@ +use rmp_serde::{decode, Serializer}; +use serde::ser::Serialize; +use std::error::Error; +use std::fmt; +use std::os::unix::net::UnixStream; +use std::path::PathBuf; +use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader}; +use tokio::task; + +use eyeballs_common::git; +use eyeballs_common::git_socket; + +#[derive(Debug)] +struct IoError { + message: String, +} + +impl IoError { + fn new(message: impl Into<String>) -> Self { + Self { + message: message.into(), + } + } +} + +impl fmt::Display for IoError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl Error for IoError {} + +#[tokio::main] +async fn main() -> Result<(), Box<dyn Error>> { + let pre = match std::env::current_exe()? + .file_name() + .and_then(|x| x.to_str()) + { + Some("pre-receive") => true, + Some("post-receive") => false, + _ => return Err(Box::<dyn Error>::from("Invalid hook executable name")), + }; + + let input = io::stdin(); + let reader = BufReader::new(input); + let mut lines = reader.lines(); + + let mut request = git_socket::GitHookRequest { + pre, + receive: Vec::new(), + }; + + let repo = git::Repository::new( + PathBuf::from("."), + true, + None::<String>, + None::<String>, + None::<PathBuf>, + ); + + while let Some(line) = lines.next_line().await? { + let data: Vec<&str> = line.split(' ').collect(); + + if data.len() == 3 { + let mut commiter: Option<String> = None; + if pre && data[1] != git::EMPTY { + if let Ok(user) = repo.get_commiter(data[1]).await { + commiter = Some(user.username); + } + } + + request.receive.push(git_socket::GitReceive { + old_value: data[0].to_string(), + new_value: data[1].to_string(), + reference: data[2].to_string(), + commiter, + }) + } + } + + let socket = PathBuf::from(repo.config_get("eyeballs.socket").await?); + + let response = task::spawn_blocking(move || { + let stream = UnixStream::connect(socket).map_err(|e| IoError::new(e.to_string()))?; + let mut serializer = Serializer::new(&stream); + request + .serialize(&mut serializer) + .map_err(|e| IoError::new(e.to_string()))?; + let result: Result<git_socket::GitHookResponse, IoError> = + decode::from_read(stream).map_err(|e| IoError::new(e.to_string())); + result + }) + .await? + .map_err(Box::<dyn Error>::from)?; + + let mut output = io::stdout(); + output.write_all(response.message.as_bytes()).await?; + + if response.ok { + Ok(()) + } else { + Err(Box::<dyn Error>::from("Hook failed")) + } +} |
