use std::sync::Mutex;
use indicatif::ProgressBar;
use systemprompt_logging::CliService;
use super::pipeline::{DeployEvent, DeployProgress};
pub struct CliDeployProgress {
spinner: Mutex<Option<ProgressBar>>,
}
impl std::fmt::Debug for CliDeployProgress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CliDeployProgress").finish_non_exhaustive()
}
}
impl CliDeployProgress {
pub const fn new() -> Self {
Self {
spinner: Mutex::new(None),
}
}
fn start_spinner(&self, message: &str) {
if let Ok(mut slot) = self.spinner.lock() {
*slot = Some(CliService::spinner(message));
}
}
fn clear_spinner(&self) {
if let Ok(mut slot) = self.spinner.lock()
&& let Some(spinner) = slot.take()
{
spinner.finish_and_clear();
}
}
}
impl Default for CliDeployProgress {
fn default() -> Self {
Self::new()
}
}
impl DeployProgress for CliDeployProgress {
fn event(&self, event: &DeployEvent<'_>) {
self.clear_spinner();
if let Some(message) = spinner_message(event) {
self.start_spinner(message);
return;
}
render_event(event);
}
}
pub const fn spinner_message(event: &DeployEvent<'_>) -> Option<&'static str> {
match event {
DeployEvent::RegistryAuthStarted => Some("Fetching registry credentials..."),
DeployEvent::BuildStarted => Some("Building Docker image..."),
DeployEvent::PushStarted => Some("Pushing to registry..."),
DeployEvent::SecretsSyncStarted => Some("Syncing secrets..."),
DeployEvent::CredentialsSyncStarted => Some("Syncing cloud credentials..."),
DeployEvent::DeployStarted => Some("Deploying..."),
_ => None,
}
}
fn render_event(event: &DeployEvent<'_>) {
match event {
DeployEvent::ArtifactsResolved {
tenant_name,
binary,
dockerfile,
} => {
CliService::key_value("Tenant", tenant_name);
CliService::key_value("Binary", &binary.display().to_string());
CliService::key_value("Dockerfile", &dockerfile.display().to_string());
},
DeployEvent::ImageResolved { image } => CliService::key_value("Image", image),
DeployEvent::BuildFinished => CliService::success("Docker image built"),
DeployEvent::PushSkipped => CliService::info("Push skipped (--skip-push)"),
DeployEvent::PushFinished => CliService::success("Image pushed"),
DeployEvent::SecretsPhaseStarted => CliService::section("Provisioning Secrets"),
DeployEvent::SecretsFileMissing => {
CliService::warning("No secrets.json found - skipping secrets sync");
},
DeployEvent::SecretsSynced { count } => {
CliService::success(&format!("Synced {} secrets", count));
},
DeployEvent::CredentialsSynced { count } => {
CliService::success(&format!("Synced {} cloud credentials", count));
},
DeployEvent::ProfilePathConfigured => CliService::success("Profile path configured"),
DeployEvent::Deployed { status, app_url } => {
CliService::success("Deployed!");
CliService::key_value("Status", status);
if let Some(url) = app_url {
CliService::key_value("URL", url);
}
},
_ => {},
}
}