Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions orion-server/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ async fn task_handler(
repo: req.repo,
target: req.target,
args: req.args,
mr: req.mr.clone().unwrap_or_default(),
};

state.clients.get(chosen_id).unwrap().send(msg).unwrap(); // TODO client maybe disconnected
Expand Down
3 changes: 2 additions & 1 deletion orion/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ once_cell = { workspace = true }
dotenvy = { workspace = true }
tokio-tungstenite = { workspace = true }
tungstenite = { workspace = true }
serde_json = { workspace = true }
serde_json = { workspace = true }
reqwest.workspace = true
2 changes: 2 additions & 0 deletions orion/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct BuildRequest {
pub repo: String,
pub target: String,
pub args: Option<Vec<String>>,
pub mr: String, // merge request id
// pub webhook: Option<String>, // post
}

Expand Down Expand Up @@ -41,6 +42,7 @@ pub async fn buck_build(
req.repo.clone(),
req.target.clone(),
req.args.unwrap_or_default(),
req.mr.clone(),
sender.clone(),
)
.await
Expand Down
43 changes: 43 additions & 0 deletions orion/src/buck_controller.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::ws::WSMessage;
use once_cell::sync::Lazy;
use serde_json::json;
use std::io;
use std::process::{ExitStatus, Stdio};
use tokio::io::AsyncBufReadExt;
Expand All @@ -9,13 +10,55 @@ use tokio::sync::mpsc::UnboundedSender;
static PROJECT_ROOT: Lazy<String> =
Lazy::new(|| std::env::var("BUCK_PROJECT_ROOT").expect("BUCK_PROJECT_ROOT must be set"));


/// Sends a filesystem mount request to the specified API endpoint
/// Parameters:
/// - repo: Repository path to mount
/// - mr: Merge request number
/// Returns: Result containing the response body string on success, or an error on failure
pub async fn mount_fs(repo: &str, mr: &str) -> Result<String, reqwest::Error> {
// Create HTTP client
let client = reqwest::Client::new();

// Construct JSON request payload
let payload = json!({
"path": repo,
"mr": mr,
});

// Send POST request
let res = client
.post("http://localhost:2725/api/fs/mount")

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded URL 'http://localhost:2725/api/fs/mount' should be configurable through environment variables or configuration files to support different deployment environments.

Suggested change
.post("http://localhost:2725/api/fs/mount")
.post(&*MOUNT_API_URL)

Copilot uses AI. Check for mistakes.
.header("Content-Type", "application/json")
.body(payload.to_string())
.send()
.await?;

// Print status code
println!("Mount request status: {}", res.status());

// Get and return response body
let body = res.text().await?;
println!("Mount response body: {body}");

Ok(body)
}

pub async fn build(
id: String,
repo: String,
target: String,
args: Vec<String>,
mr: String,
sender: UnboundedSender<WSMessage>,
) -> io::Result<ExitStatus> {

tracing::info!("Building {} in repo {} with target {}", id, repo, target);
// Prepare the command to run
// Note: `args` is a list of additional arguments to pass to the `buck

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is incomplete - it ends abruptly with 'the `buck'. Consider completing this comment or removing it.

Suggested change
// Note: `args` is a list of additional arguments to pass to the `buck
// Note: `args` is a list of additional arguments to pass to the `buck` command,
// which is used to build the specified target in the given repository.

Copilot uses AI. Check for mistakes.

let _ = mount_fs(&repo, &mr).await;

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silently ignoring the result of mount_fs could hide important errors. Consider handling the error appropriately, either by logging it or propagating it to the caller.

Suggested change
let _ = mount_fs(&repo, &mr).await;
if let Err(e) = mount_fs(&repo, &mr).await {
tracing::error!("Failed to mount filesystem for repo {} and MR {}: {}", repo, mr, e);
}

Copilot uses AI. Check for mistakes.

let mut cmd = Command::new("buck2");
let cmd = cmd
.arg("build")
Expand Down
34 changes: 30 additions & 4 deletions orion/src/ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub enum WSMessage {
repo: String,
target: String,
args: Option<Vec<String>>,
mr: String,
},
TaskAck {
id: String,
Expand All @@ -36,6 +37,30 @@ pub enum WSMessage {
},
}

const MAX_RETRIES: u32 = 3;

/// send message and retry
async fn send_with_retry(sender: &mut (impl SinkExt<Message> + Unpin), msg: &WSMessage) -> Result<(), String> {
let msg_str = serde_json::to_string(msg)
.map_err(|e| format!("seriaz fail: {e}"))?;
let ws_msg = Message::Text(Utf8Bytes::from(msg_str));

for attempt in 0..=MAX_RETRIES {
match sender.send(ws_msg.clone()).await {
Ok(_) => return Ok(()),
Err(_) => {
if attempt == MAX_RETRIES {
return Err(format!("max retry times:({MAX_RETRIES}), "));
}
println!("send fail (retry {}/{})", attempt + 1, MAX_RETRIES);
const RETRY_DELAY_MS: u64 = 200;
tokio::time::sleep(std::time::Duration::from_millis(RETRY_DELAY_MS)).await;
}
}
}
Err("max retry error".into())
}

pub async fn spawn_client(server: &str) {
let ws_stream = match connect_async(server).await {
Ok((stream, response)) => {
Expand All @@ -55,8 +80,8 @@ pub async fn spawn_client(server: &str) {

let mut send_task = tokio::spawn(async move {
while let Some(msg) = rx.recv().await {
let msg = serde_json::to_string(&msg).unwrap();
if let Err(e) = sender.send(Message::Text(Utf8Bytes::from(msg))).await {
//let msg = serde_json::to_string(&msg).unwrap();
if let Err(e) = send_with_retry(&mut sender, &msg).await {
println!("Error sending message: {e}");
break;
}
Expand Down Expand Up @@ -91,11 +116,12 @@ async fn process_message(msg: Message) -> ControlFlow<(), ()> {
repo,
target,
args,
mr,
} => {
println!(">>> got task: id:{id}, repo:{repo}, target:{target}, args:{args:?}");
println!(">>> got task: id:{id}, repo:{repo}, target:{target}, args:{args:?}, mr:{mr}");
let Json(res) = buck_build(
id.parse().unwrap(),
BuildRequest { repo, target, args },
BuildRequest { repo, target, args, mr },
SENDER.get().unwrap().clone(),
)
.await;
Expand Down
Loading