1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
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;
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 {}
#[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>);
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 {
match repo.get_commiter(data[1]).await {
Ok(user) => {
commiter = Some(user.username);
}
Err(_) => {}
}
}
request.receive.push(git_socket::GitReceive {
old_value: data[0].to_string(),
new_value: data[1].to_string(),
reference: data[2].to_string(),
commiter: 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"))
}
}
|