use crate::config::GlobalConfig;
use crate::upgrade::{SelfUpdater, backup::BackupManager, version_check::VersionChecker};
use anyhow::{Context, Result, bail};
use clap::Parser;
use colored::Colorize;
use std::env;
use tracing::debug;
#[derive(Parser, Debug)]
pub struct UpgradeArgs {
#[arg(value_name = "VERSION")]
pub version: Option<String>,
#[arg(long)]
pub check: bool,
#[arg(short, long)]
pub status: bool,
#[arg(short, long)]
pub force: bool,
#[arg(long)]
pub rollback: bool,
#[arg(long)]
pub no_backup: bool,
}
pub async fn execute(args: UpgradeArgs) -> Result<()> {
let _config = GlobalConfig::load().await?;
let current_exe = env::current_exe().context("Failed to get current executable path")?;
if args.rollback {
return handle_rollback(¤t_exe).await;
}
let updater = SelfUpdater::new().force(args.force);
let version_checker = VersionChecker::new().await?;
if args.status {
return show_status(&updater, &version_checker).await;
}
if args.check {
return check_for_updates(&updater, &version_checker).await;
}
perform_upgrade(
&updater,
&version_checker,
¤t_exe,
args.version.as_deref(),
args.no_backup,
)
.await
}
async fn handle_rollback(current_exe: &std::path::Path) -> Result<()> {
println!("{}", "Rolling back to previous version...".yellow());
let backup_manager = BackupManager::new(current_exe.to_path_buf());
if !backup_manager.backup_exists() {
bail!("No backup found. Cannot rollback.");
}
backup_manager.restore_backup().await.context("Failed to restore from backup")?;
println!("{}", "Successfully rolled back to previous version".green());
Ok(())
}
async fn show_status(updater: &SelfUpdater, version_checker: &VersionChecker) -> Result<()> {
let current_version = updater.current_version();
let latest_version = match version_checker.check_now().await {
Ok(version) => version,
Err(e) => {
debug!("Failed to check for updates: {}", e);
None
}
};
let info = VersionChecker::format_version_info(current_version, latest_version.as_deref());
println!("{info}");
Ok(())
}
async fn check_for_updates(updater: &SelfUpdater, version_checker: &VersionChecker) -> Result<()> {
println!("{}", "Checking for updates...".cyan());
match version_checker.check_now().await {
Ok(Some(latest_version)) => {
println!(
"{}",
format!("Update available: {} -> {}", updater.current_version(), latest_version)
.green()
);
println!("Run `agpm upgrade` to install the latest version");
}
Ok(None) => {
println!(
"{}",
format!("You are on the latest version ({})", updater.current_version()).green()
);
}
Err(e) => {
bail!("Failed to check for updates: {e}");
}
}
Ok(())
}
async fn perform_upgrade(
updater: &SelfUpdater,
version_checker: &VersionChecker,
current_exe: &std::path::Path,
target_version: Option<&str>,
no_backup: bool,
) -> Result<()> {
let backup_manager = if no_backup {
None
} else {
println!("{}", "Creating backup...".cyan());
let manager = BackupManager::new(current_exe.to_path_buf());
manager.create_backup().await.context("Failed to create backup")?;
Some(manager)
};
let upgrade_msg = if let Some(version) = target_version {
format!("Upgrading to version {version}...").cyan()
} else {
"Upgrading to latest version...".cyan()
};
println!("{upgrade_msg}");
let result = if let Some(version) = target_version {
updater.update_to_version(version).await
} else {
updater.update_to_latest().await
};
match result {
Ok(true) => {
version_checker.clear_cache().await?;
println!("{}", "Upgrade completed successfully!".green());
if let Some(manager) = backup_manager
&& let Err(e) = manager.cleanup_backup().await
{
debug!("Failed to cleanup backup: {}", e);
}
}
Ok(false) => {
println!(
"{}",
format!("Already on the latest version ({})", updater.current_version()).green()
);
}
Err(e) => {
if let Some(manager) = backup_manager {
println!("{}", "Upgrade failed. Attempting to restore backup...".red());
if let Err(restore_err) = manager.restore_backup().await {
eprintln!("{}", format!("Failed to restore backup: {restore_err}").red());
eprintln!("Backup is located at: {}", manager.backup_path().display());
} else {
println!("{}", "Successfully restored from backup".green());
}
}
bail!("Upgrade failed: {e}");
}
}
Ok(())
}