ssmm 0.6.0

AWS SSM Parameter Store helper for team-scoped .env sync (systemd friendly)
use anyhow::{Context, Result, anyhow, bail};
use colored::Colorize;
use std::path::PathBuf;

use crate::cli::MigrateToExecArgs;
use crate::config::prefix_root;
use crate::systemd::{SystemdScope, build_drop_in};

pub fn cmd_migrate_to_exec(args: MigrateToExecArgs) -> Result<()> {
    let MigrateToExecArgs {
        unit,
        app,
        exec_cmd,
        system,
        keep_env_files,
        pre_execs,
        ssmm_bin,
        apply,
    } = args;
    let scope = if system {
        SystemdScope::System
    } else {
        SystemdScope::User
    };

    let ssmm_bin = ssmm_bin
        .or_else(|| {
            std::env::var("HOME")
                .ok()
                .map(|h| PathBuf::from(h).join(".cargo/bin/ssmm"))
        })
        .ok_or_else(|| anyhow!("cannot resolve default ssmm bin path (HOME unset)"))?;
    if !ssmm_bin.is_absolute() {
        bail!("--ssmm-bin must be an absolute path: {:?}", ssmm_bin);
    }

    let drop_in_dir = scope.drop_in_dir(&unit)?;
    let drop_in_path = drop_in_dir.join("exec-mode.conf");

    let content = build_drop_in(
        &app,
        &exec_cmd,
        &keep_env_files,
        &pre_execs,
        &ssmm_bin,
        prefix_root(),
    );

    if !apply {
        println!(
            "# dry-run: would write {}",
            drop_in_path.display().to_string().bold()
        );
        println!(
            "# apply with: `ssmm migrate-to-exec ... --apply`  (writes file + daemon-reload)"
        );
        println!(
            "# revert with: `rm {} && systemctl {} daemon-reload`",
            drop_in_path.display(),
            scope.as_cli_flag()
        );
        println!();
        print!("{}", content);
        return Ok(());
    }

    if drop_in_path.exists() {
        eprintln!(
            "  {} {} already exists; overwriting",
            "warning:".yellow().bold(),
            drop_in_path.display()
        );
    }

    std::fs::create_dir_all(&drop_in_dir)
        .with_context(|| format!("mkdir -p {}", drop_in_dir.display()))?;
    std::fs::write(&drop_in_path, &content)
        .with_context(|| format!("write {}", drop_in_path.display()))?;
    println!("  ✓ wrote {}", drop_in_path.display());

    let status = std::process::Command::new("systemctl")
        .arg(scope.as_cli_flag())
        .arg("daemon-reload")
        .status()
        .context("spawn systemctl daemon-reload")?;
    if !status.success() {
        bail!(
            "`systemctl {} daemon-reload` exited with {}",
            scope.as_cli_flag(),
            status
        );
    }
    println!("  ✓ systemctl {} daemon-reload", scope.as_cli_flag());

    println!();
    println!(
        "revert: rm {} && systemctl {} daemon-reload",
        drop_in_path.display(),
        scope.as_cli_flag()
    );

    Ok(())
}