flow-iron 0.4.3

Infrastructure-as-code CLI — deploy Docker Compose apps with Caddy reverse proxy and Cloudflare DNS
Documentation
use anyhow::Result;
use comfy_table::{Table, modifiers::UTF8_ROUND_CORNERS, presets::UTF8_FULL_CONDENSED};
use console::style;
use std::collections::HashMap;

use crate::config::Fleet;
use crate::ssh::SshPool;
use crate::ui;

pub async fn run(fleet: &Fleet, server_filter: Option<&str>) -> Result<()> {
    let filtered: HashMap<String, _> = fleet
        .servers
        .iter()
        .filter(|(name, _)| server_filter.is_none() || server_filter == Some(name.as_str()))
        .map(|(k, v)| (k.clone(), v.clone()))
        .collect();

    if filtered.is_empty() {
        anyhow::bail!("No matching server found");
    }

    let sp = ui::spinner("Connecting...");
    let pool = SshPool::connect(&filtered).await?;
    sp.finish_and_clear();

    for name in filtered.keys() {
        ui::header(&format!("Server: {name}"));

        let output = pool
            .exec(
                name,
                "docker ps -a --format '{{.Names}}\t{{.Status}}\t{{.Image}}\t{{.Ports}}\t{{.Size}}' 2>/dev/null || echo ''",
            )
            .await?;

        let mut table = Table::new();
        table
            .load_preset(UTF8_FULL_CONDENSED)
            .apply_modifier(UTF8_ROUND_CORNERS)
            .set_header(vec![
                style("Container").bold().to_string(),
                style("Status").bold().to_string(),
                style("Image").bold().to_string(),
                style("Ports").bold().to_string(),
                style("Size").bold().to_string(),
            ]);

        for line in output.lines() {
            if line.is_empty() {
                continue;
            }
            let parts: Vec<&str> = line.splitn(5, '\t').collect();
            if parts.len() == 5 {
                let status_colored = if parts[1].starts_with("Up") {
                    style(parts[1]).green().to_string()
                } else {
                    style(parts[1]).red().to_string()
                };
                table.add_row(vec![
                    parts[0].to_string(),
                    status_colored,
                    parts[2].to_string(),
                    parts[3].to_string(),
                    parts[4].to_string(),
                ]);
            }
        }

        println!("{table}");
    }

    pool.close().await?;
    Ok(())
}