[WIP] client action streaming

This commit is contained in:
2026-04-01 20:23:56 -05:00
parent 4b525f4641
commit 104dcbb1b1
14 changed files with 1152 additions and 828 deletions

View File

@@ -12,10 +12,10 @@
//! 1. SIGTERM is sent to the process immediately
//! 2. After a 5-second grace period, SIGKILL is sent as a last resort
use super::{BoundedLogWriter, ExecutionResult, OutputFormat, RuntimeResult};
use super::{BoundedLogFileWriter, BoundedLogWriter, ExecutionResult, OutputFormat, RuntimeResult};
use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::time::Instant;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::process::Command;
@@ -59,6 +59,8 @@ pub async fn execute_streaming(
max_stderr_bytes,
output_format,
None,
None,
None,
)
.await
}
@@ -93,6 +95,8 @@ pub async fn execute_streaming_cancellable(
max_stderr_bytes: usize,
output_format: OutputFormat,
cancel_token: Option<CancellationToken>,
stdout_log_path: Option<&Path>,
stderr_log_path: Option<&Path>,
) -> RuntimeResult<ExecutionResult> {
let start = Instant::now();
@@ -130,6 +134,8 @@ pub async fn execute_streaming_cancellable(
// Create bounded writers
let mut stdout_writer = BoundedLogWriter::new_stdout(max_stdout_bytes);
let mut stderr_writer = BoundedLogWriter::new_stderr(max_stderr_bytes);
let mut stdout_file = open_live_log_file(stdout_log_path, max_stdout_bytes, true).await?;
let mut stderr_file = open_live_log_file(stderr_log_path, max_stderr_bytes, false).await?;
// Take stdout and stderr streams
let stdout = child.stdout.take().expect("stdout not captured");
@@ -150,6 +156,9 @@ pub async fn execute_streaming_cancellable(
if stdout_writer.write_all(&line).await.is_err() {
break;
}
if let Some(file) = stdout_file.as_mut() {
let _ = file.write_all(&line).await;
}
}
Err(_) => break,
}
@@ -167,6 +176,9 @@ pub async fn execute_streaming_cancellable(
if stderr_writer.write_all(&line).await.is_err() {
break;
}
if let Some(file) = stderr_file.as_mut() {
let _ = file.write_all(&line).await;
}
}
Err(_) => break,
}
@@ -351,6 +363,24 @@ pub async fn execute_streaming_cancellable(
})
}
async fn open_live_log_file(
path: Option<&Path>,
max_bytes: usize,
is_stdout: bool,
) -> io::Result<Option<BoundedLogFileWriter>> {
let Some(path) = path else {
return Ok(None);
};
let path: PathBuf = path.to_path_buf();
let writer = if is_stdout {
BoundedLogFileWriter::new_stdout(&path, max_bytes).await?
} else {
BoundedLogFileWriter::new_stderr(&path, max_bytes).await?
};
Ok(Some(writer))
}
/// Parse stdout content according to the specified output format.
fn configure_child_process(cmd: &mut Command) -> io::Result<()> {
#[cfg(unix)]