feat: use UDP rendezvous for git pre-push hook
Replaces the Python-based pre-push hook and long-polling HTTP logic with a native shell script using udp-send --wait to block on an ephemeral UDP port in the kernel. mcp-memory-server acts as the broker and replies to subscribers.
This commit is contained in:
1 parent
f4dcf3acbf
commit
8bc8c97504
2 files changed
+83
-4
No files matched your search
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Native shell pre-push hook using UDP Rendezvous
|
||||||
|
|
||||||
|
PORT=${MCP_UDP_PORT1:-3001}
|
||||||
|
|
||||||
|
while read local_ref local_sha remote_ref remote_sha; do
|
||||||
|
if [ "$local_sha" = "0000000000000000000000000000000000000000" ] || [ "$local_ref" = "(delete)" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ "$local_ref" == refs/tags/* ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
BRANCH=${local_ref#refs/heads/}
|
||||||
|
|
||||||
|
echo -e "\033[36m[Pre-Push Gatekeeper]\033[0m Checking gate for branch '$BRANCH'..."
|
||||||
|
|
||||||
|
# Check if gate is already authorized or blocked
|
||||||
|
if mcp-memory-server gate verify --action push --target "$BRANCH" --consume 2>/dev/null; then
|
||||||
|
echo -e "\033[32m[Pre-Push Gatekeeper]\033[0m Push AUTHORIZED by MCP Memory gate for branch '$BRANCH'."
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
EXIT_CODE=$?
|
||||||
|
if [ $EXIT_CODE -eq 1 ]; then
|
||||||
|
echo -e "\033[31m[Pre-Push Gatekeeper]\033[0m Push BLOCKED by MCP Memory gate for branch '$BRANCH'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If exit code is 2 (Not Found), we wait via UDP Rendezvous
|
||||||
|
echo -e "\033[33m[Pre-Push Gatekeeper]\033[0m No pre-authorized gate for '$BRANCH'. Waiting for approval via UDP rendezvous..."
|
||||||
|
|
||||||
|
# Payload for UDP
|
||||||
|
PAYLOAD="{\"type\":\"gate_wait\",\"action\":\"push\",\"target\":\"$BRANCH\"}"
|
||||||
|
|
||||||
|
if udp-send --wait "$PORT" "$PAYLOAD"; then
|
||||||
|
echo -e "\033[32m[Pre-Push Gatekeeper]\033[0m Push AUTHORIZED via UDP rendezvous for branch '$BRANCH'."
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
echo -e "\033[31m[Pre-Push Gatekeeper]\033[0m Push DENIED or timed out via UDP rendezvous for branch '$BRANCH'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
+38
-4
@@ -335,12 +335,18 @@ pub async fn run_server(state: Arc<MemoryState>) -> Result<(), Box<dyn std::erro
|
|||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let port1 = std::env::var("MCP_UDP_PORT1").unwrap_or_else(|_| "3001".to_string());
|
let port1 = std::env::var("MCP_UDP_PORT1").unwrap_or_else(|_| "3001".to_string());
|
||||||
if let Ok(socket) = tokio::net::UdpSocket::bind(format!("127.0.0.1:{}", port1)).await {
|
if let Ok(socket) = tokio::net::UdpSocket::bind(format!("127.0.0.1:{}", port1)).await {
|
||||||
|
let socket = Arc::new(socket);
|
||||||
|
let socket_rx = socket.clone();
|
||||||
|
|
||||||
|
let mut subscribers: HashMap<(String, String), std::net::SocketAddr> = HashMap::new();
|
||||||
let mut buf = vec![0u8; 65536];
|
let mut buf = vec![0u8; 65536];
|
||||||
|
let mut event_rx = udp_state.handler.state.event_bus_tx.subscribe();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Ok((len, _addr)) = socket.recv_from(&mut buf).await
|
tokio::select! {
|
||||||
&& let Ok(payload) =
|
recv_res = socket_rx.recv_from(&mut buf) => {
|
||||||
serde_json::from_slice::<crate::models::TerminalHistory>(&buf[..len])
|
if let Ok((len, addr)) = recv_res {
|
||||||
{
|
if let Ok(payload) = serde_json::from_slice::<crate::models::TerminalHistory>(&buf[..len]) {
|
||||||
udp_state
|
udp_state
|
||||||
.handler
|
.handler
|
||||||
.state
|
.state
|
||||||
@@ -362,6 +368,34 @@ pub async fn run_server(state: Arc<MemoryState>) -> Result<(), Box<dyn std::erro
|
|||||||
for tx in senders {
|
for tx in senders {
|
||||||
let _ = tx.try_send(msg_str.clone());
|
let _ = tx.try_send(msg_str.clone());
|
||||||
}
|
}
|
||||||
|
} else if let Ok(gate_wait) = serde_json::from_slice::<serde_json::Value>(&buf[..len]) {
|
||||||
|
if gate_wait.get("type").and_then(|t| t.as_str()) == Some("gate_wait") {
|
||||||
|
if let (Some(action), Some(target)) = (
|
||||||
|
gate_wait.get("action").and_then(|a| a.as_str()),
|
||||||
|
gate_wait.get("target").and_then(|t| t.as_str())
|
||||||
|
) {
|
||||||
|
subscribers.insert((action.to_string(), target.to_string()), addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(event) = event_rx.recv() => {
|
||||||
|
if event.topic == "gate:event" {
|
||||||
|
if let (Some(action), Some(target), Some(status)) = (
|
||||||
|
event.payload.get("action").and_then(|a| a.as_str()),
|
||||||
|
event.payload.get("target").and_then(|t| t.as_str()),
|
||||||
|
event.payload.get("status").and_then(|s| s.as_str()),
|
||||||
|
) {
|
||||||
|
if status == "authorized" || status == "blocked" {
|
||||||
|
if let Some(addr) = subscribers.remove(&(action.to_string(), target.to_string())) {
|
||||||
|
let response = if status == "authorized" { b"APPROVED" } else { b"REJECTED" };
|
||||||
|
let _ = socket.send_to(response, addr).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user