mecha10-cli 0.8.0

Mecha10 CLI tool
Documentation
//! Ps command handler
//!
//! Lists running processes/containers for a target - pairs with `handlers::run`/`handlers::stop`
//! (see `commands::ps::PsCommand`'s doc comment for the target-argument shape this mirrors).

use crate::commands::PsCommand;
use crate::context::CliContext;
use crate::paths;
use crate::services::DockerService;
use anyhow::{Context, Result};

remote_dispatch_handler!(handle_ps, PsCommand, ps_remote_target);

/// List running containers in the remote target's Docker Compose stack (ported from the former
/// `mecha10 remote ps`)
async fn ps_remote_target(ctx: &mut CliContext) -> Result<()> {
    let compose_path = ctx.working_dir.join(paths::docker::COMPOSE_REMOTE_FILE);
    if !compose_path.exists() {
        println!("No remote compose file found.");
        return Ok(());
    }

    let compose_file_path = compose_path.to_string_lossy().to_string();
    let docker = DockerService::with_compose_file(&compose_file_path);
    docker.check_installation()?;

    let status = std::process::Command::new("docker")
        .arg("compose")
        .arg("-f")
        .arg(&compose_file_path)
        .arg("ps")
        .status()
        .context("Failed to run docker compose ps")?;

    if !status.success() {
        return Err(anyhow::anyhow!("docker compose ps failed"));
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::context::CliContextBuilder;
    use tempfile::tempdir;

    fn ctx_in(dir: &std::path::Path) -> CliContext {
        CliContextBuilder::new()
            .working_dir(dir.to_path_buf())
            .config_path(Some(dir.join("mecha10.json")))
            .build()
            .unwrap()
    }

    #[tokio::test]
    async fn handle_ps_remote_ok_when_no_compose_file() {
        let dir = tempdir().unwrap();
        let mut ctx = ctx_in(dir.path());

        assert!(handle_ps(&mut ctx, &PsCommand::Remote).await.is_ok());
    }
}