use std::{env, process::Command};
use clap::{crate_version, Args};
use color_eyre::eyre::{bail, Ok, Result};
use fluent_templates::Loader;
use semver::Version;
use tracing::error;
use url::Url;
use crate::{
utils::{self, CurrentDir},
LANG_ID, LOCALES,
};
#[must_use]
#[derive(Args)]
#[command(about = LOCALES.lookup(&LANG_ID, "update_command"))]
pub struct Update {
#[arg(long, num_args = 0..=1, default_missing_value = super::DEFAULT_PROXY_SURGE,
help = LOCALES.lookup(&LANG_ID, "proxy"))]
pub proxy: Option<Url>,
}
pub async fn execute(config: Update) -> Result<()> {
utils::ensure_executable_exists("gh")?;
if let Some(proxy) = config.proxy {
env::set_var("HTTP_PROXY", proxy.to_string());
env::set_var("HTTPS_PROXY", proxy.to_string());
}
let current_version = Version::parse(crate_version!())?;
println!("Checking current version: {}", current_version);
let latest_released_version = latest_released_version()?;
println!(
"Checking latest released version: {}",
latest_released_version
);
if current_version >= latest_released_version {
println!("Already up-to-date");
return Ok(());
}
let zip_name = if cfg!(target_os = "windows") {
"novel-cli-x86_64-pc-windows-msvc.zip"
} else if cfg!(target_os = "macos") {
"novel-cli-aarch64-apple-darwin.zip"
} else if cfg!(target_os = "linux") {
"novel-cli-x86_64-unknown-linux-gnu.zip"
} else {
bail!("Unsupported operating system");
};
let temp_dir = tempfile::tempdir()?;
let current_dir = CurrentDir::new(temp_dir.path())?;
let mut child = Command::new("gh")
.arg("release")
.arg("download")
.arg("--clobber")
.args(["--pattern", zip_name])
.args(["--repo", "novel-rs/cli"])
.spawn()?;
if !child.wait()?.success() {
bail!("`gh` failed to execute");
}
let zip_path = temp_dir.path().join(zip_name);
super::unzip(&zip_path)?;
let file_name = if cfg!(target_os = "windows") {
"novel-cli.exe"
} else {
"novel-cli"
};
let file_path = temp_dir
.path()
.join(zip_path.with_extension(""))
.join(file_name);
self_replace::self_replace(file_path)?;
current_dir.restore()?;
println!("Update Successful");
Ok(())
}
fn latest_released_version() -> Result<Version> {
let output = Command::new("gh")
.arg("release")
.arg("list")
.args(["--repo", "novel-rs/cli"])
.args(["--json", "tagName"])
.args(["--order", "desc"])
.args(["--jq", ".[0].tagName"])
.output()?;
if !output.status.success() {
error!("{}", simdutf8::basic::from_utf8(&output.stderr)?);
bail!("`gh` failed to execute");
}
Ok(Version::parse(
simdutf8::basic::from_utf8(&output.stdout)?.trim(),
)?)
}