summaryrefslogtreecommitdiff
path: root/server/hook
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2025-02-01 22:42:11 +0100
committerJoel Klinghed <the_jk@spawned.biz>2025-02-01 22:42:11 +0100
commitd780391408b9e6d443e5e4f907748cae484b79fb (patch)
treed961efd62478248081d1a327c818d6fa171f0a2d /server/hook
parent05b674190f26e2a58cc7b7288586c031552d50f3 (diff)
Use workspace instead of features
Having to include --feature=build-server in basically all commands that wasn't building eyeballs-githook got tiring quickly. Instead, use workspaces, with a separate project for building the githook. It means I also had to add a library common with code shared by both githook and server.
Diffstat (limited to 'server/hook')
-rw-r--r--server/hook/Cargo.toml14
-rw-r--r--server/hook/src/githook.rs105
2 files changed, 119 insertions, 0 deletions
diff --git a/server/hook/Cargo.toml b/server/hook/Cargo.toml
new file mode 100644
index 0000000..2a298b7
--- /dev/null
+++ b/server/hook/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "eyeballs-githook"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+eyeballs-common = { path = "../common" }
+rmp-serde.workspace = true
+serde.workspace = true
+tokio = { workspace = true, features = ["full"] }
+
+[[bin]]
+name = "eyeballs-githook"
+path = "src/githook.rs"
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"))
+ }
+}