diff options
Diffstat (limited to 'server/src/githook.rs')
| -rw-r--r-- | server/src/githook.rs | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/server/src/githook.rs b/server/src/githook.rs new file mode 100644 index 0000000..057ee47 --- /dev/null +++ b/server/src/githook.rs @@ -0,0 +1,108 @@ +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 users::get_current_username; + +mod fs_utils; +mod git; +mod 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 {} + +async fn get_socket() -> Result<String, git::Error> { + let repo = git::Repository::new(PathBuf::from("."), true, None::<String>, None::<String>); + repo.config_get("eyeballs.socket").await +} + +#[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 user = match get_current_username() { + Some(username) => match username.into_string() { + Ok(valid_username) => valid_username, + Err(_) => return Err(Box::<dyn Error>::from("Invalid username for current user")), + }, + None => { + return Err(Box::<dyn Error>::from( + "Unable to get username of current user", + )) + } + }; + + let input = io::stdin(); + let reader = BufReader::new(input); + let mut lines = reader.lines(); + + let mut request = git_socket::GitHookRequest { + pre, + user, + receive: Vec::new(), + }; + while let Some(line) = lines.next_line().await? { + let data: Vec<&str> = line.split(' ').collect(); + + if data.len() == 3 { + request.receive.push(git_socket::GitReceive { + old_value: data[0].to_string(), + new_value: data[1].to_string(), + reference: data[2].to_string(), + }) + } + } + + let socket = PathBuf::from(get_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")) + } +} |
