use std::path::Path;
use colored::Colorize;
use crate::commands::discord_notify::{
notify_discord_for_project, oci_notification, DiscordStatus,
};
use crate::strategies::{ServiceConfig, XbpConfig};
use super::plan::DeployPlan;
use super::providers::execute_step;
use super::types::{DeployExecutionReport, DeployMode};
pub async fn execute_plan(
project_root: &Path,
config: &XbpConfig,
plan: &DeployPlan,
mode: DeployMode,
yes: bool,
debug: bool,
) -> Result<DeployExecutionReport, String> {
let mut lines = Vec::new();
let mut failed = Vec::new();
lines.push(format!(
"{} {} services mode={mode:?}",
"Executing".bright_cyan().bold(),
plan.steps.len()
));
for step in &plan.steps {
println!(
"{} {} ({})",
"→".bright_blue().bold(),
step.service.bright_white().bold(),
step.provider.bright_yellow()
);
let service_cfg: Option<ServiceConfig> = config
.services
.as_ref()
.and_then(|services| services.iter().find(|s| s.name == step.service).cloned());
match execute_step(project_root, config, step, mode, yes, debug).await {
Ok(step_lines) => {
for line in step_lines {
println!(" {line}");
lines.push(line);
}
if let Some(image_ref) = step.image_ref.as_deref() {
if matches!(mode, DeployMode::Run | DeployMode::Verify) {
notify_discord_for_project(
project_root,
Some(config),
service_cfg.as_ref(),
oci_notification(
&step.service,
image_ref,
DiscordStatus::Success,
&format!(
"OCI image verified/deployed (provider={}, mode={:?})",
step.provider, mode
),
),
)
.await;
}
}
}
Err(error) => {
let msg = format!("[{}] ERROR: {error}", step.service);
println!(" {}", msg.bright_red());
lines.push(msg);
failed.push(step.service.clone());
if let Some(image_ref) = step.image_ref.as_deref() {
notify_discord_for_project(
project_root,
Some(config),
service_cfg.as_ref(),
oci_notification(
&step.service,
image_ref,
DiscordStatus::Failure,
&error,
),
)
.await;
}
}
}
}
Ok(DeployExecutionReport {
ok: failed.is_empty(),
lines,
failed_services: failed,
})
}