use crate::Result;
use crate::cli::CleanArgs;
use crate::config::GlobalConfig;
use crate::config::Paths;
use crate::docker::Engine;
use colored::Colorize;
use std::io::{self, Write};
pub fn execute_clean(args: &CleanArgs, verbose: bool) -> Result<()> {
let paths = Paths::new()?;
let global_config = GlobalConfig::load(&paths.global_config())?;
let engine = global_config.get_container_engine()?;
if args.danger_purge_all_images {
return danger_purge_all_images(&paths, &engine);
}
remove_images(verbose, &paths, &engine)?;
remove_dangling_images(&paths, &engine);
remove_containers(verbose, &paths, &engine);
Ok(())
}
fn danger_purge_all_images(paths: &Paths, engine: &Engine) -> Result<()> {
eprintln!(
"{}: This will remove:\
- all stopped containers\
- all networks not used by at least one container\
- all images without at least one container associated to them\
- all build cache",
"WARNING".red().bold()
);
eprint!("Are you sure? Type 'yes' to confirm: ");
io::stdout().flush().ok();
let mut input = String::new();
io::stdin().read_line(&mut input)?;
if input.trim() != "yes" {
println!("Aborted.");
return Ok(());
}
println!("{}", "Removing all images...".yellow());
let status = crate::docker::execute_command_status(
engine,
["image", "prune", "-af"],
&paths.flyboat_dir(),
)?;
if status.success() {
println!("{} All images removed", "✓".green());
}
Ok(())
}
fn remove_images(verbose: bool, paths: &Paths, engine: &Engine) -> Result<()> {
if verbose {
eprintln!("Removing flyboat images...");
}
let images_output = crate::docker::execute_command_output(
engine,
[
"images",
"--filter",
"reference=flyboat-*",
"--format",
"{{.ID}}",
],
&paths.flyboat_dir(),
)?;
let image_ids = String::from_utf8_lossy(&images_output.stdout);
let image_ids: Vec<&str> = image_ids.lines().filter(|s| !s.is_empty()).collect();
if !image_ids.is_empty() {
println!(
"{} {} flyboat image(s)...",
"Removing".blue(),
image_ids.len()
);
for id in &image_ids {
if verbose {
eprintln!(" Removing image {}", id);
}
crate::docker::execute_command_status(engine, ["rmi", id], &paths.flyboat_dir()).ok();
}
println!(
"{} Removed {} flyboat image(s)",
"✓".green(),
image_ids.len()
);
} else {
println!("No flyboat images to remove");
}
Ok(())
}
fn remove_dangling_images(paths: &Paths, engine: &Engine) {
println!("Removing dangling images...");
let status =
crate::docker::execute_command_status(engine, ["image", "prune"], &paths.flyboat_dir())
.ok();
if status.is_some_and(|s| s.success()) {
println!("{} Dangling images removed", "✓".green());
}
}
fn remove_containers(verbose: bool, paths: &Paths, engine: &Engine) {
if verbose {
eprintln!("Removing flyboat containers...");
}
let containers_output = crate::docker::execute_command_output(
engine,
["ps", "-aq", "--filter", "name=flyboat-"],
&paths.flyboat_dir(),
);
match containers_output {
Ok(output) => {
let container_ids = String::from_utf8_lossy(&output.stdout);
let container_ids: Vec<&str> =
container_ids.lines().filter(|s| !s.is_empty()).collect();
if !container_ids.is_empty() {
println!(
"{} {} container(s)...",
"Removing".red(),
container_ids.len()
);
for id in &container_ids {
crate::docker::execute_command_status(engine, ["rm", id], &paths.flyboat_dir())
.ok();
}
println!("Removed {} container(s)", container_ids.len());
}
}
Err(err) => {
eprintln!("Error: Could not remove containers: {err}");
}
}
}