use std::env;
use std::fmt::Write;
use anyhow::Result;
use axoupdater::{AxoUpdater, AxoupdateError, UpdateRequest};
use owo_colors::OwoColorize;
use tracing::{debug, enabled};
use crate::cli::ExitStatus;
use crate::install_source::InstallSource;
use crate::printer::Printer;
fn format_install_hint() -> String {
match InstallSource::detect() {
Some(s) => format!(
"{}{} You installed prek via {}. To update, run `{}`",
"hint".cyan().bold(),
":".bold(),
s.description(),
s.update_instructions()
),
None => format!(
"{}{} If you installed prek with pip, brew, or another package manager, update prek with `pip install --upgrade`, `brew upgrade`, or similar.",
"hint".cyan().bold(),
":".bold()
),
}
}
pub(crate) async fn self_update(
version: Option<String>,
token: Option<String>,
printer: Printer,
) -> Result<ExitStatus> {
let mut updater = AxoUpdater::new_for("prek");
if enabled!(tracing::Level::DEBUG) {
unsafe { env::set_var("INSTALLER_PRINT_VERBOSE", "1") };
updater.enable_installer_output();
} else {
updater.disable_installer_output();
}
if let Some(ref token) = token {
updater.set_github_token(token);
}
let Ok(updater) = updater.load_receipt() else {
debug!("no receipt found; assuming prek was installed via a package manager");
writeln!(
printer.stderr(),
"{}{} Self-update is only available for prek binaries installed via the standalone installation scripts.",
"error".red().bold(),
":".bold(),
)?;
writeln!(printer.stderr(), "{}", format_install_hint())?;
return Ok(ExitStatus::Error);
};
if !updater.check_receipt_is_for_this_executable()? {
debug!(
"receipt is not for this executable; assuming prek was installed via a package manager"
);
writeln!(
printer.stderr(),
"{}{} Self-update is only available for prek binaries installed via the standalone installation scripts.",
"error".red().bold(),
":".bold(),
)?;
writeln!(printer.stderr(), "{}", format_install_hint())?;
return Ok(ExitStatus::Error);
}
writeln!(
printer.stderr(),
"{}",
format_args!(
"{}{} Checking for updates...",
"info".cyan().bold(),
":".bold()
)
)?;
let update_request = if let Some(version) = version {
UpdateRequest::SpecificTag(version)
} else {
UpdateRequest::Latest
};
updater.configure_version_specifier(update_request);
match updater.run().await {
Ok(Some(result)) => {
let version_information = if let Some(old_version) = result.old_version {
format!(
"from {} to {}",
format!("v{old_version}").bold().white(),
format!("v{}", result.new_version).bold().white(),
)
} else {
format!("to {}", format!("v{}", result.new_version).bold().white())
};
writeln!(
printer.stderr(),
"{}",
format_args!(
"{}{} Upgraded prek {}! {}",
"success".green().bold(),
":".bold(),
version_information,
format!(
"https://github.com/j178/prek/releases/tag/{}",
result.new_version_tag
)
.cyan()
)
)?;
}
Ok(None) => {
writeln!(
printer.stderr(),
"{}",
format_args!(
"{}{} You're on the latest version of prek ({})",
"success".green().bold(),
":".bold(),
format!("v{}", env!("CARGO_PKG_VERSION")).bold().white()
)
)?;
}
Err(err) => {
return if let AxoupdateError::Reqwest(err) = err {
if err.status() == Some(http::StatusCode::FORBIDDEN) && token.is_none() {
writeln!(
printer.stderr(),
"{}",
format_args!(
"{}{} GitHub API rate limit exceeded. Please provide a GitHub token via the {} option.",
"error".red().bold(),
":".bold(),
"`--token`".green().bold()
)
)?;
Ok(ExitStatus::Error)
} else {
Err(err.into())
}
} else {
Err(err.into())
};
}
}
Ok(ExitStatus::Success)
}