lmrc-cli 0.3.16

CLI tool for scaffolding LMRC Stack infrastructure projects
Documentation
use clap::{Parser, Subcommand};
use lmrc_config_validator::LmrcConfig;
use lmrc_pipeline::steps::*;
use lmrc_pipeline::{Pipeline, PipelineContext, StepRegistry};
use std::path::Path;

#[derive(Parser)]
#[command(name = "pipeline")]
#[command(about = "LMRC Stack deployment pipeline", version)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Check code formatting and linting
    Check {
        /// Run format check
        #[arg(long)]
        format: bool,

        /// Run clippy
        #[arg(long)]
        clippy: bool,
    },

    /// Run tests
    Test,

    /// Build applications
    Build {
        /// Build in release mode
        #[arg(long)]
        release: bool,
    },

    /// Build Docker images
    DockerBuild,

    /// Provision infrastructure (servers, networks, etc.)
    Provision,

    /// Setup infrastructure (K8s, databases, etc.)
    Setup,

    /// Deploy applications
    Deploy,

    /// Run full pipeline (provision + setup + deploy)
    Full,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let cli = Cli::parse();

    // Load configuration
    let config = LmrcConfig::from_file(Path::new("lmrc.toml"))?;

    // Execute command
    match cli.command {
        Commands::Check { format, clippy } => {
            let ctx = PipelineContext::new(config)?;
            let mut pipeline = Pipeline::new(ctx);

            if format {
                pipeline = pipeline.add_step(FormatCheckStep::new());
            }
            if clippy {
                pipeline = pipeline.add_step(ClippyStep::new());
            }

            // If no flags specified, run both
            if !format && !clippy {
                pipeline = pipeline
                    .add_step(FormatCheckStep::new())
                    .add_step(ClippyStep::new());
            }

            pipeline.run().await?;
        }

        Commands::Test => {
            let ctx = PipelineContext::new(config)?;
            Pipeline::new(ctx).add_step(TestStep::new()).run().await?;
        }

        Commands::Build { release } => {
            let mut step = BuildStep::new();
            if release {
                step = step.release();
            }

            let ctx = PipelineContext::new(config)?;
            Pipeline::new(ctx).add_step(step).run().await?;
        }

        Commands::DockerBuild => {
            let ctx = PipelineContext::new(config)?;
            Pipeline::new(ctx)
                .add_step(DockerBuildStep::new())
                .run()
                .await?;
        }

        Commands::Provision => {
            let ctx = PipelineContext::new(config)?;
            Pipeline::new(ctx)
                .add_step(ProvisionStep::new())
                .run()
                .await?;
        }

        Commands::Setup => {
            // Create registry and dynamically build setup steps based on config
            let registry = StepRegistry::default();
            let setup_steps = registry.build_setup_steps(&config);

            let ctx = PipelineContext::new(config)?;
            Pipeline::new(ctx)
                .add_steps_arc(setup_steps)
                .run()
                .await?;
        }

        Commands::Deploy => {
            let ctx = PipelineContext::new(config)?;
            Pipeline::new(ctx)
                .add_step(DeployStep::new())
                .run()
                .await?;
        }

        Commands::Full => {
            // Create registry and dynamically build steps based on config
            let registry = StepRegistry::default();

            let ctx = PipelineContext::new(config.clone())?;
            let mut pipeline = Pipeline::new(ctx);

            // Add build steps (format, clippy, test, build)
            pipeline = pipeline.add_steps_arc(registry.build_build_steps(&config));

            // Add Docker build
            pipeline = pipeline.add_step(DockerBuildStep::new());

            // Add infrastructure provision step
            pipeline = pipeline.add_steps_arc(registry.build_provision_steps(&config));

            // Add setup steps (dynamically based on config)
            pipeline = pipeline.add_steps_arc(registry.build_setup_steps(&config));

            // Add deploy steps
            pipeline = pipeline.add_steps_arc(registry.build_deploy_steps(&config));

            pipeline.run().await?;
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_cli_structure() {
        // Verify CLI can be instantiated
        use clap::CommandFactory;
        Cli::command();
    }

    #[test]
    fn test_all_commands_exist() {
        use clap::CommandFactory;
        let cmd = Cli::command();

        let subcommands: Vec<_> = cmd.get_subcommands().map(|s| s.get_name()).collect();

        assert!(subcommands.contains(&"check"));
        assert!(subcommands.contains(&"test"));
        assert!(subcommands.contains(&"build"));
        assert!(subcommands.contains(&"docker-build"));
        assert!(subcommands.contains(&"provision"));
        assert!(subcommands.contains(&"setup"));
        assert!(subcommands.contains(&"deploy"));
        assert!(subcommands.contains(&"full"));
    }
}