diff --git a/src/cli/commands/self_install.rs b/src/cli/commands/self_install.rs index 6a2c597..84eb4a1 100644 --- a/src/cli/commands/self_install.rs +++ b/src/cli/commands/self_install.rs @@ -1,8 +1,7 @@ -use crate::cli::{version::replace_bin_exe, HOME_DIR}; +use crate::cli::{version::replace_pesde_bin_exe, HOME_DIR}; use anyhow::Context; use clap::Args; use colored::Colorize; -use pesde::engine::EngineKind; use std::env::current_exe; #[derive(Debug, Args)] @@ -72,11 +71,7 @@ and then restart your shell. ); } - replace_bin_exe( - EngineKind::Pesde, - ¤t_exe().context("failed to get current exe path")?, - ) - .await?; + replace_pesde_bin_exe(¤t_exe().context("failed to get current exe path")?).await?; Ok(()) } diff --git a/src/cli/commands/self_upgrade.rs b/src/cli/commands/self_upgrade.rs index f355e82..0ad5f6a 100644 --- a/src/cli/commands/self_upgrade.rs +++ b/src/cli/commands/self_upgrade.rs @@ -1,9 +1,11 @@ -use crate::cli::{ - config::read_config, - version::{ - current_version, find_latest_version, get_or_download_engine, no_build_metadata, - replace_bin_exe, +use crate::{ + cli::{ + config::read_config, + version::{ + current_version, find_latest_version, get_or_download_engine, replace_pesde_bin_exe, + }, }, + util::no_build_metadata, }; use anyhow::Context; use clap::Args; @@ -54,7 +56,7 @@ impl SelfUpgradeCommand { VersionReq::parse(&format!("={latest_version}")).unwrap(), ) .await?; - replace_bin_exe(EngineKind::Pesde, &path).await?; + replace_pesde_bin_exe(&path).await?; println!("upgraded to version {display_latest_version}!"); diff --git a/src/cli/install.rs b/src/cli/install.rs index 1891267..98c0305 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -200,6 +200,21 @@ pub async fn install( let root_progress = root_progress; root_progress.set_prefix(format!("{} {}: ", manifest.name, manifest.target)); + #[cfg(feature = "version-management")] + { + root_progress.set_message("update engine linkers"); + + let mut tasks = manifest + .engines + .keys() + .map(|engine| crate::cli::version::make_linker_if_needed(*engine)) + .collect::>(); + + while let Some(task) = tasks.join_next().await { + task.unwrap()?; + } + } + root_progress.set_message("clean"); if options.write { diff --git a/src/cli/reporters.rs b/src/cli/reporters.rs index 61f3c6e..8f88768 100644 --- a/src/cli/reporters.rs +++ b/src/cli/reporters.rs @@ -109,7 +109,7 @@ pub struct CliDownloadProgressReporter { impl DownloadsReporter for CliReporter { type DownloadProgressReporter = CliDownloadProgressReporter; - fn report_download<'b>(self: Arc, name: String) -> Self::DownloadProgressReporter { + fn report_download(self: Arc, name: String) -> Self::DownloadProgressReporter { self.root_progress.inc_length(1); CliDownloadProgressReporter { diff --git a/src/cli/version.rs b/src/cli/version.rs index 9a7b252..3d5f59f 100644 --- a/src/cli/version.rs +++ b/src/cli/version.rs @@ -1,9 +1,12 @@ -use crate::cli::{ - bin_dir, - config::{read_config, write_config, CliConfig}, - files::make_executable, - home_dir, - reporters::run_with_reporter, +use crate::{ + cli::{ + bin_dir, + config::{read_config, write_config, CliConfig}, + files::make_executable, + home_dir, + reporters::run_with_reporter, + }, + util::no_build_metadata, }; use anyhow::Context; use colored::Colorize; @@ -32,12 +35,6 @@ pub fn current_version() -> Version { Version::parse(env!("CARGO_PKG_VERSION")).unwrap() } -pub fn no_build_metadata(version: &Version) -> Version { - let mut version = version.clone(); - version.build = semver::BuildMetadata::EMPTY; - version -} - const CHECK_INTERVAL: chrono::Duration = chrono::Duration::hours(6); pub async fn find_latest_version(reqwest: &reqwest::Client) -> anyhow::Result { @@ -249,23 +246,18 @@ pub async fn get_or_download_engine( .await .context("failed to make downloaded version executable")?; - // replace the executable if there isn't any installed, or the one installed is out of date - if installed_versions.pop_last().is_none_or(|v| version > v) { - replace_bin_exe( - engine, - ¤t_exe().context("failed to get current exe path")?, - ) - .await?; + if engine != EngineKind::Pesde { + make_linker_if_needed(engine).await?; } Ok(path) } #[instrument(level = "trace")] -pub async fn replace_bin_exe(engine: EngineKind, with: &Path) -> anyhow::Result<()> { +pub async fn replace_pesde_bin_exe(with: &Path) -> anyhow::Result<()> { let bin_exe_path = bin_dir() .await? - .join(engine.to_string()) + .join(EngineKind::Pesde.to_string()) .with_extension(std::env::consts::EXE_EXTENSION); let exists = bin_exe_path.exists(); @@ -295,3 +287,25 @@ pub async fn replace_bin_exe(engine: EngineKind, with: &Path) -> anyhow::Result< make_executable(&bin_exe_path).await } + +#[instrument(level = "trace")] +pub async fn make_linker_if_needed(engine: EngineKind) -> anyhow::Result<()> { + let bin_dir = bin_dir().await?; + let linker = bin_dir + .join(engine.to_string()) + .with_extension(std::env::consts::EXE_EXTENSION); + let exists = linker.exists(); + + if !exists { + let exe = current_exe().context("failed to get current exe path")?; + + #[cfg(windows)] + let result = fs::symlink_file(exe, linker); + #[cfg(not(windows))] + let result = fs::symlink(exe, linker); + + result.await.context("failed to create symlink")?; + } + + Ok(()) +} diff --git a/src/engine/source/github/mod.rs b/src/engine/source/github/mod.rs index 7663d3c..316e95e 100644 --- a/src/engine/source/github/mod.rs +++ b/src/engine/source/github/mod.rs @@ -8,6 +8,7 @@ use crate::{ traits::{DownloadOptions, EngineSource, ResolveOptions}, }, reporters::{response_to_async_read, DownloadProgressReporter}, + util::no_build_metadata, version_matches, }; use reqwest::header::ACCEPT; @@ -80,14 +81,21 @@ impl EngineSource for GitHubEngineSource { .. } = options; - let desired_asset_name = self - .asset_template - .replace("{VERSION}", &version.to_string()); + let desired_asset_names = [ + self.asset_template + .replace("{VERSION}", &version.to_string()), + self.asset_template + .replace("{VERSION}", &no_build_metadata(version).to_string()), + ]; let asset = engine_ref .assets .iter() - .find(|asset| asset.name.eq_ignore_ascii_case(&desired_asset_name)) + .find(|asset| { + desired_asset_names + .iter() + .any(|name| asset.name.eq_ignore_ascii_case(name)) + }) .ok_or(errors::DownloadError::AssetNotFound)?; reporter.report_start(); diff --git a/src/main.rs b/src/main.rs index ad1d541..c9592ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -138,12 +138,17 @@ impl<'a> MakeWriter<'a> for IndicatifWriter { async fn run() -> anyhow::Result<()> { let cwd = std::env::current_dir().expect("failed to get current working directory"); let current_exe = std::env::current_exe().expect("failed to get current executable path"); - let exe_name = current_exe.file_stem().unwrap(); + let exe_name = current_exe + .file_stem() + .unwrap() + .to_str() + .expect("exe name is not valid utf-8"); + let exe_name_engine = EngineKind::from_str(exe_name); #[cfg(windows)] 'scripts: { - // we're called the same as the binary, so we're not a (legal) script - if exe_name == env!("CARGO_PKG_NAME") { + // if we're an engine, we don't want to run any scripts + if exe_name_engine.is_ok() { break 'scripts; } @@ -273,10 +278,7 @@ async fn run() -> anyhow::Result<()> { #[cfg(feature = "version-management")] 'engines: { - let Some(engine) = exe_name - .to_str() - .and_then(|str| EngineKind::from_str(str).ok()) - else { + let Ok(engine) = exe_name_engine else { break 'engines; }; diff --git a/src/util.rs b/src/util.rs index c80ac09..5cf4e20 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,6 @@ use crate::AuthConfig; use gix::bstr::BStr; +use semver::Version; use serde::{Deserialize, Deserializer, Serializer}; use sha2::{Digest, Sha256}; use std::collections::{BTreeMap, HashSet}; @@ -88,3 +89,9 @@ pub fn hash>(struc: S) -> String { pub fn is_default(t: &T) -> bool { t == &T::default() } + +pub fn no_build_metadata(version: &Version) -> Version { + let mut version = version.clone(); + version.build = semver::BuildMetadata::EMPTY; + version +}