mecha10-deploy 0.7.0

Mecha10 deploy services — Docker management, binary distribution, remote deployment
Documentation
//! Deployment service types

use mecha10_core::enum_str_method;
use std::path::PathBuf;
use std::str::FromStr;

#[derive(Debug, Clone)]
pub struct DeployConfig {
    pub host: String,
    pub user: String,
    pub port: u16,
    pub ssh_key: Option<PathBuf>,
    pub remote_dir: String,
    pub service_name: String,
    pub backup_dir: String,
    pub health_check_timeout: u64,
    pub health_check_retries: u32,
}

impl Default for DeployConfig {
    fn default() -> Self {
        Self {
            host: "robot.local".to_string(),
            user: "mecha10".to_string(),
            port: 22,
            ssh_key: None,
            remote_dir: "/opt/mecha10".to_string(),
            service_name: "mecha10-robot".to_string(),
            backup_dir: "/opt/mecha10/backups".to_string(),
            health_check_timeout: 30,
            health_check_retries: 3,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeployStrategy {
    Direct,
    Rolling,
    BlueGreen,
}

impl FromStr for DeployStrategy {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "direct" => Ok(DeployStrategy::Direct),
            "rolling" => Ok(DeployStrategy::Rolling),
            "blue-green" | "bluegreen" => Ok(DeployStrategy::BlueGreen),
            _ => Err(format!("Invalid deployment strategy: {}", s)),
        }
    }
}

enum_str_method!(
    pub fn as_str(DeployStrategy) -> &str {
        Direct => "direct",
        Rolling => "rolling",
        BlueGreen => "blue-green",
    }
);

#[derive(Debug, Clone)]
pub struct DeployResult {
    pub success: bool,
    pub message: String,
    pub health_check_passed: bool,
    pub backup_created: bool,
}

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

    #[test]
    fn deploy_strategy_parses_remaining_strategies() {
        assert_eq!("direct".parse::<DeployStrategy>(), Ok(DeployStrategy::Direct));
        assert_eq!("rolling".parse::<DeployStrategy>(), Ok(DeployStrategy::Rolling));
        assert_eq!("blue-green".parse::<DeployStrategy>(), Ok(DeployStrategy::BlueGreen));
        assert_eq!("bluegreen".parse::<DeployStrategy>(), Ok(DeployStrategy::BlueGreen));
    }

    #[test]
    fn deploy_strategy_rejects_removed_canary_strategy() {
        assert!("canary".parse::<DeployStrategy>().is_err());
    }

    #[test]
    fn deploy_strategy_rejects_unknown_strategy() {
        assert!("invalid".parse::<DeployStrategy>().is_err());
    }

    #[test]
    fn deploy_strategy_as_str_matches_from_str() {
        for (s, expected) in [
            ("direct", DeployStrategy::Direct),
            ("rolling", DeployStrategy::Rolling),
            ("blue-green", DeployStrategy::BlueGreen),
        ] {
            let parsed: DeployStrategy = s.parse().unwrap();
            assert_eq!(parsed, expected);
            assert_eq!(parsed.as_str(), s);
        }
    }
}