From 81ffaafef7e1e18351e817ea1bf7a0cb1cd8b207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Misty=20De=20M=C3=A9o?= Date: Wed, 18 Dec 2024 14:14:19 -0800 Subject: [PATCH] feat: check multiple receipt paths We now check multiple receipt paths, including XDG_CONFIG_HOME. We load a receipt from the first path which contains a receipt file, ensuring we're able to manage the absence of a receipt in that path if that environment variable is set. --- axoupdater/src/errors.rs | 10 +++++++ axoupdater/src/receipt.rs | 50 ++++++++++++++++++++++++---------- axoupdater/src/test/helpers.rs | 7 +++-- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/axoupdater/src/errors.rs b/axoupdater/src/errors.rs index 2a5eea2..014b6d2 100644 --- a/axoupdater/src/errors.rs +++ b/axoupdater/src/errors.rs @@ -85,6 +85,16 @@ pub enum AxoupdateError { app_name: String, }, + /// Not a generic receipt load failure, but the receipt itself doesn't exist. + #[error("Unable to load receipt for app {app_name}")] + #[diagnostic(help( + "This may indicate that this installation of {app_name} was installed via a method that's not eligible for upgrades." + ))] + NoReceipt { + /// This app's name + app_name: String, + }, + /// Indicates that this app's name couldn't be determined when trying /// to autodetect it. #[error("Unable to determine the name of the app to update")] diff --git a/axoupdater/src/receipt.rs b/axoupdater/src/receipt.rs index 03e9a0c..c4e16d3 100644 --- a/axoupdater/src/receipt.rs +++ b/axoupdater/src/receipt.rs @@ -116,49 +116,69 @@ impl AxoUpdater { } } -pub(crate) fn get_config_path(app_name: &str) -> AxoupdateResult { +/// Returns a Vec of possible receipt locations, beginning with +/// `XDG_CONFIG_HOME` (if set). +pub(crate) fn get_config_paths(app_name: &str) -> AxoupdateResult> { + let mut potential_homes = vec![]; + if env::var("AXOUPDATER_CONFIG_WORKING_DIR").is_ok() { - Ok(Utf8PathBuf::try_from(current_dir()?)?) + Ok(vec![Utf8PathBuf::try_from(current_dir()?)?]) } else if let Ok(path) = env::var("AXOUPDATER_CONFIG_PATH") { - Ok(Utf8PathBuf::from(path)) + Ok(vec![Utf8PathBuf::from(path)]) } else { let xdg_home = env::var("XDG_CONFIG_HOME") .ok() - .map(PathBuf::from) + .map(Utf8PathBuf::from) .map(|h| h.join(app_name)); - let xdg_home_exists = xdg_home.as_ref().map(|h| h.exists()).unwrap_or(false); - + if let Some(home) = &xdg_home { + if home.exists() { + potential_homes.push(home.to_owned()); + } + } let home = if cfg!(windows) { env::var("LOCALAPPDATA") .map(PathBuf::from) .map(|h| h.join(app_name)) .ok() - } else if xdg_home_exists { - xdg_home } else { homedir::my_home()?.map(|path| path.join(".config").join(app_name)) }; - let Some(home) = home else { + if let Some(home) = home { + potential_homes.push(Utf8PathBuf::try_from(home)?); + } + + if potential_homes.is_empty() { return Err(AxoupdateError::NoHome {}); - }; + } - Ok(Utf8PathBuf::try_from(home)?) + Ok(potential_homes) } } +/// Iterates through the list of possible receipt locations from +/// `get_config_paths` and returns the first that contains a valid receipt. +pub(crate) fn get_receipt_path(app_name: &str) -> AxoupdateResult> { + for receipt_prefix in get_config_paths(app_name)? { + let install_receipt_path = receipt_prefix.join(format!("{app_name}-receipt.json")); + if install_receipt_path.exists() { + return Ok(Some(install_receipt_path)); + } + } + + Ok(None) +} + fn load_receipt_from_path(install_receipt_path: &Utf8PathBuf) -> AxoupdateResult { Ok(SourceFile::load_local(install_receipt_path)?.deserialize_json()?) } fn load_receipt_for(app_name: &str) -> AxoupdateResult { - let Ok(receipt_prefix) = get_config_path(app_name) else { - return Err(AxoupdateError::ConfigFetchFailed { + let Some(install_receipt_path) = get_receipt_path(app_name)? else { + return Err(AxoupdateError::NoReceipt { app_name: app_name.to_owned(), }); }; - let install_receipt_path = receipt_prefix.join(format!("{app_name}-receipt.json")); - load_receipt_from_path(&install_receipt_path).map_err(|_| AxoupdateError::ReceiptLoadFailed { app_name: app_name.to_owned(), }) diff --git a/axoupdater/src/test/helpers.rs b/axoupdater/src/test/helpers.rs index edf64f7..b57432a 100644 --- a/axoupdater/src/test/helpers.rs +++ b/axoupdater/src/test/helpers.rs @@ -3,7 +3,7 @@ use std::{ process::{Command, Stdio}, }; -use crate::{receipt::get_config_path, ReleaseSourceType}; +use crate::{receipt::get_receipt_path, ReleaseSourceType}; static RECEIPT_TEMPLATE: &str = r#"{"binaries":[BINARIES],"install_prefix":"INSTALL_PREFIX","provider":{"source":"cargo-dist","version":"0.10.0-prerelease.1"},"source":{"app_name":"APP_NAME","name":"PACKAGE","owner":"OWNER","release_type":"RELEASE_TYPE"},"version":"VERSION"}"#; @@ -104,7 +104,10 @@ pub fn perform_runtest(runtest_args: &RuntestArgs) -> PathBuf { let app_home = &home.join(".cargo").join("bin"); let app_path = &app_home.join(basename); - let config_path = get_config_path(app_name).unwrap().into_std_path_buf(); + let config_path = get_receipt_path(app_name) + .unwrap() + .unwrap() + .into_std_path_buf(); // Ensure we delete any previous copy that may exist // at this path before we copy in our version.