diff --git a/src/bisect.rs b/src/bisect.rs index a66c929..1b3f043 100644 --- a/src/bisect.rs +++ b/src/bisect.rs @@ -107,6 +107,8 @@ pub fn process_job(job: Job, conn: &Connection) -> Result<()> { trace!(?bisect, "Finished bisection job"); + crate::toolchain::clean_toolchains()?; + Ok(()) } diff --git a/src/main.rs b/src/main.rs index 340911e..50ad1a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod bisect; mod db; +mod toolchain; use std::sync::{mpsc, Arc, Mutex}; @@ -50,6 +51,8 @@ async fn main() -> color_eyre::Result<()> { db::setup(&worker_conn).wrap_err("db setup")?; + toolchain::clean_toolchains()?; + let app = Router::new() .route("/", get(|| async { index_html() })) .route("/bisect/:id", get(get_bisection)) diff --git a/src/toolchain.rs b/src/toolchain.rs new file mode 100644 index 0000000..97b0ae5 --- /dev/null +++ b/src/toolchain.rs @@ -0,0 +1,73 @@ +use std::process::Command; + +use color_eyre::{ + eyre::{eyre, Context}, + Result, +}; +use tracing::debug; + +const MAX_BISECTOR_TOOLCHAINS: usize = 15; + +#[tracing::instrument] +pub fn clean_toolchains() -> Result<()> { + let toolchains = get_toolchains()?; + let for_removal = filter_toolchain_for_removal(toolchains); + if !for_removal.is_empty() { + remove_toolchains(&for_removal)?; + } + + Ok(()) +} + +fn filter_toolchain_for_removal(mut toolchains: Vec) -> Vec { + toolchains.retain(|toolchain| toolchain.starts_with("bisector-")); + + let amount = toolchains.len(); + if amount <= MAX_BISECTOR_TOOLCHAINS { + debug!(%amount, "No toolchains removed"); + return Vec::new(); + } + + let to_remove = amount - MAX_BISECTOR_TOOLCHAINS; + + toolchains.into_iter().take(to_remove).collect() +} + +fn get_toolchains() -> Result> { + let mut command = Command::new("rustup"); + command.args(["toolchain", "list"]); + + let output = command + .output() + .wrap_err("running `rustup toolchain list`")?; + + if output.status.success() { + let stdout = + String::from_utf8(output.stdout).wrap_err("rustup returned non-utf-8 bytes")?; + + let toolchains = stdout.lines().map(ToOwned::to_owned).collect(); + + Ok(toolchains) + } else { + let stderr = String::from_utf8_lossy(&output.stderr).into_owned(); + Err(eyre!("`rustup toolchain list` failed").wrap_err(stderr)) + } +} + +fn remove_toolchains(toolchains: &[String]) -> Result<()> { + debug!(?toolchains, "Removing toolchains"); + let mut command = Command::new("rustup"); + command.args(["toolchain", "remove"]); + command.args(toolchains); + + let output = command + .output() + .wrap_err("running `rustup toolchain remove`")?; + + if output.status.success() { + return Ok(()); + } + + let stderr = String::from_utf8_lossy(&output.stderr).into_owned(); + Err(eyre!("`rustup toolchain remove` failed").wrap_err(stderr)) +}