flyboat 2.0.0

Container environment manager for development
Documentation
use crate::cli::BuildArgs;
use crate::config::GlobalConfig;
use crate::docker::Engine;
use crate::docker::build::{BuildCommand, arch_to_platform, image_name};
use crate::environment::EnvironmentManager;
use crate::template::{ProcessContext, process_templates};
use crate::{EnvFeaturizedName, Error, Result};
use colored::Colorize;

pub fn execute_build(args: &BuildArgs, verbose: bool) -> Result<()> {
    let manager = EnvironmentManager::new()?;
    let paths = manager.paths();

    // Load global config for defaults
    let global_config = GlobalConfig::load(&paths.global_config())?;

    // Parse environment name with features (name+features)
    let env_feat_name = EnvFeaturizedName::parse(&args.env)?;

    // Find environment (error if not found)
    let env = manager.search_result(&env_feat_name.name)?;

    // Clone config and apply features
    let mut config = env.config.clone();
    if !env_feat_name.features.is_empty() {
        eprintln!("Taking base configuration '{}'", env_feat_name.name.green());
        for feature in &env_feat_name.features {
            eprintln!("  - Applying feature '{}'", feature.cyan());
        }
        config.apply_features(&env_feat_name.features)?;
    }

    if verbose {
        eprintln!("Building environment: {}", env.name.green());
    }

    // Determine engine
    let engine = match &args.engine {
        Some(e) => e.parse::<Engine>()?,
        None => global_config.get_container_engine()?,
    };

    // Determine and validate architecture
    let arch = args.arch.as_deref().or(global_config.arch.as_deref());

    // Validate architecture if specified
    let platform = if let Some(a) = arch {
        Some(arch_to_platform(a).ok_or_else(|| Error::InvalidArchitecture(a.to_string()))?)
    } else {
        None
    };

    // Generate image name
    let img_name = image_name(&env.name, arch);

    // Build command
    let build_cmd = BuildCommand {
        engine,
        image_name: img_name.clone(),
        context_path: env.path.display().to_string(),
        dockerfile_path: paths.dockerfile(&env.env_path()).display().to_string(),
        platform,
        no_cache: args.rebuild,
    };

    if args.dry_run {
        println!("{}", "Dry run - would execute:".blue());
        println!("  {}", build_cmd);
        return Ok(());
    }

    // Process templates
    if !config.templates.is_empty() {
        if verbose {
            eprintln!("{} Processing templates...", "".blue());
        }
        process_templates(&env.path, &config.templates, ProcessContext::Build, verbose)?;
    }

    println!(
        "{} {} with {}",
        "Building".blue(),
        img_name.green().bold(),
        engine.to_string().cyan()
    );

    if verbose {
        eprintln!("  Command: {}", build_cmd);
    }

    // Execute build
    let status = crate::docker::execute_command_status(&engine, build_cmd.args(), &env.path)?;

    if !status.success() {
        return Err(Error::ContainerCommandFailed(format!(
            "Build failed with exit code: {:?}",
            status.code()
        )));
    }

    println!("{} Image {} built successfully", "".green(), img_name);

    Ok(())
}