use std::{
process::Command,
thread::sleep,
time::{Duration, Instant},
};
use radicle_ci_broker::timeoutcmd::TimeoutCommand;
use super::*;
/// Trigger a CI run.
///
/// This is meant for developer experimentation.
#[derive(Parser)]
pub struct TimeoutCmd {
/// A Bash script to run. Should start with "exec", or time out won't work.
#[clap(long)]
script: String,
/// Text to be fed to script via stdin.
#[clap(long, default_value = "")]
stdin: String,
/// Generate at least this much data to feed to script via stdin.
#[clap(long)]
generate: Option<usize>,
/// Terminate script after this many seconds.
#[clap(long)]
timeout: u64,
/// Verbose output: show stdout and stderr output lines.
#[clap(short, long)]
verbose: bool,
/// Don't empty stdout and stderr buffers, let them fill up.
#[clap(long)]
fill_buffers: bool,
/// Kill script after this many seconds, unconditionally.
#[clap(long)]
kill_after: Option<u64>,
}
impl Leaf for TimeoutCmd {
fn run(&self, _args: &Args) -> Result<(), CibToolError> {
let mut cmd = Command::new("bash");
cmd.arg("-c").arg(&self.script);
let mut to = TimeoutCommand::new(Duration::from_secs(self.timeout));
let mut stdout = to.stdout();
if let Some(bytes) = self.generate {
let mut stdin: Vec<u8> = vec![];
while stdin.len() < bytes {
for byte in b"hello, world\n" {
stdin.push(*byte);
}
}
to.feed_stdin(stdin.as_slice());
println!("generated stdin has {} bytes", stdin.len());
} else {
to.feed_stdin(self.stdin.as_bytes());
}
let started = Instant::now();
println!("spawn child");
let running = to.spawn(cmd)?;
let mut stdout_bytes = 0;
let tor = if let Some(secs) = self.kill_after {
sleep(Duration::from_secs(secs));
running.kill()?
} else {
if !self.fill_buffers {
while let Some(line) = stdout.line() {
stdout_bytes += line.len();
if self.verbose {
println!("stdout: {line:?}");
}
}
println!("finished reading stdout");
}
running.wait()?
};
let stderr = tor.stderr();
for line in stderr {
if self.verbose {
println!("stderr: {line:?}");
}
}
println!("finished reading stderr");
let elapsed = started.elapsed();
let speed = (stdout_bytes as f64) / elapsed.as_secs_f64();
println!("stdout bytes: {stdout_bytes}");
println!("duration: {} ms", elapsed.as_millis());
println!("speed: {speed:.0} B/s");
println!("exit: {}", tor.exit_code());
Ok(())
}
}