From eac2314e6ccb4ba91b7272523e68110035167279 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Tue, 26 May 2026 22:51:28 +0300 Subject: fix: handle package-manager remote updates --- src/remote.rs | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 234 insertions(+), 11 deletions(-) (limited to 'src/remote.rs') diff --git a/src/remote.rs b/src/remote.rs index 9a4cc774..3cf1e43b 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -300,9 +300,63 @@ impl RemoteHerdr { #[derive(Deserialize)] struct RemoteUpdateManifest { version: String, + protocol: Option, + assets: BTreeMap, + #[serde(default, deserialize_with = "deserialize_remote_manifest_releases")] + releases: BTreeMap, +} + +#[derive(Deserialize)] +struct RemoteReleaseMetadata { + protocol: Option, + #[serde(default)] assets: BTreeMap, } +fn deserialize_remote_manifest_releases<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let value = Option::::deserialize(deserializer)?; + Ok(match value { + Some(serde_json::Value::Object(object)) => object + .into_iter() + .filter_map(|(version, release)| { + serde_json::from_value::(release) + .ok() + .map(|metadata| (version, metadata)) + }) + .collect(), + _ => BTreeMap::new(), + }) +} + +impl RemoteUpdateManifest { + fn release_for_version(&self, version: &str) -> Option> { + if self.version.trim_start_matches('v') == version { + return Some(RemoteManifestReleaseRef { + protocol: self.protocol, + assets: &self.assets, + }); + } + + self.releases.get(version).and_then(|release| { + (!release.assets.is_empty()).then_some(RemoteManifestReleaseRef { + protocol: release.protocol, + assets: &release.assets, + }) + }) + } +} + +#[derive(Clone, Copy)] +struct RemoteManifestReleaseRef<'a> { + protocol: Option, + assets: &'a BTreeMap, +} + struct InstallSource { path: PathBuf, temporary_dir: Option, @@ -525,11 +579,23 @@ fn remote_binary_override_path() -> io::Result> { } fn install_source_description(platform: &RemotePlatform, override_binary: Option<&Path>) -> String { + install_source_description_for( + platform, + override_binary, + local_binary_can_seed_remote(platform), + ) +} + +fn install_source_description_for( + platform: &RemotePlatform, + override_binary: Option<&Path>, + local_binary_can_seed_remote: bool, +) -> String { if let Some(path) = override_binary { return format!("{REMOTE_BINARY_ENV_VAR} ({})", path.display()); } - if *platform == RemotePlatform::local() { + if local_binary_can_seed_remote { "the current local herdr binary".to_string() } else { format!( @@ -549,12 +615,24 @@ fn resolve_install_source( if *platform == RemotePlatform::local() { let path = std::env::current_exe()?; - return Ok(InstallSource::persistent(path)); + if !crate::update::is_package_manager_managed_exe_path(&path) { + return Ok(InstallSource::persistent(path)); + } } download_release_asset(platform) } +fn local_binary_can_seed_remote(platform: &RemotePlatform) -> bool { + if *platform != RemotePlatform::local() { + return false; + } + + std::env::current_exe() + .map(|path| !crate::update::is_package_manager_managed_exe_path(&path)) + .unwrap_or(false) +} + #[derive(Debug, Clone, PartialEq, Eq)] enum RemoteServerStatus { Running { @@ -985,16 +1063,22 @@ fn download_release_asset(platform: &RemotePlatform) -> io::Result