auto_update: Improve Linux rsync hinting (#50637)
Previously, automatic checks could fail quietly and return to idle. This change ensures missing dependency errors are surfaced in the update UI with install guidance. What Changed: - Added a dedicated `MissingDependencyError` for dependency-related update failures. - Updated auto-update polling behavior so: - automatic checks still stay quiet for transient/general errors, - but missing dependency errors are surfaced as `AutoUpdateStatus::Errored`. - Improved Linux dependency hinting: - distro checks with `/etc/os-release`, - returns distro-appropriate install hints where possible and falls back to a generic package-manager message when distro parsing is unavailable/unknown. <img width="610" height="145" alt="image" src="https://github.com/user-attachments/assets/8bef3970-38ba-4412-9ece-7b6bb6bf903b" /> Closes #47552 - [x] Done a self-review taking into account security and performance aspects Release Notes: - Improved Linux auto-update failure issue caused by missing `rsync` by surfacing actionable install guidance in the update UI.
This commit is contained in:
1 parent
900269bf7f
commit
e6c20cbe0f
1 file changed
+74
-1
@@ -30,9 +30,64 @@ use util::command::new_command;
|
||||
use workspace::Workspace;
|
||||
|
||||
const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MissingDependencyError(String);
|
||||
|
||||
impl std::fmt::Display for MissingDependencyError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for MissingDependencyError {}
|
||||
const POLL_INTERVAL: Duration = Duration::from_secs(60 * 60);
|
||||
const REMOTE_SERVER_CACHE_LIMIT: usize = 5;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn linux_rsync_install_hint() -> &'static str {
|
||||
let os_release = match std::fs::read_to_string("/etc/os-release") {
|
||||
Ok(os_release) => os_release,
|
||||
Err(_) => return "Please install rsync using your package manager",
|
||||
};
|
||||
|
||||
let mut distribution_ids = Vec::new();
|
||||
for line in os_release.lines() {
|
||||
let trimmed = line.trim();
|
||||
if let Some(value) = trimmed.strip_prefix("ID=") {
|
||||
distribution_ids.push(value.trim_matches('"').to_ascii_lowercase());
|
||||
} else if let Some(value) = trimmed.strip_prefix("ID_LIKE=") {
|
||||
for id in value.trim_matches('"').split_whitespace() {
|
||||
distribution_ids.push(id.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let package_manager_hint = if distribution_ids
|
||||
.iter()
|
||||
.any(|distribution_id| distribution_id == "arch")
|
||||
{
|
||||
Some("Install it with: sudo pacman -S rsync")
|
||||
} else if distribution_ids
|
||||
.iter()
|
||||
.any(|distribution_id| distribution_id == "debian" || distribution_id == "ubuntu")
|
||||
{
|
||||
Some("Install it with: sudo apt install rsync")
|
||||
} else if distribution_ids.iter().any(|distribution_id| {
|
||||
distribution_id == "fedora"
|
||||
|| distribution_id == "rhel"
|
||||
|| distribution_id == "centos"
|
||||
|| distribution_id == "rocky"
|
||||
|| distribution_id == "almalinux"
|
||||
}) {
|
||||
Some("Install it with: sudo dnf install rsync")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
package_manager_hint.unwrap_or("Please install rsync using your package manager")
|
||||
}
|
||||
|
||||
actions!(
|
||||
auto_update,
|
||||
[
|
||||
@@ -397,7 +452,15 @@ impl AutoUpdater {
|
||||
this.update(cx, |this, cx| {
|
||||
this.pending_poll = None;
|
||||
if let Err(error) = result {
|
||||
let is_missing_dependency =
|
||||
error.downcast_ref::<MissingDependencyError>().is_some();
|
||||
this.status = match check_type {
|
||||
UpdateCheckType::Automatic if is_missing_dependency => {
|
||||
log::warn!("auto-update: {}", error);
|
||||
AutoUpdateStatus::Errored {
|
||||
error: Arc::new(error),
|
||||
}
|
||||
}
|
||||
// Be quiet if the check was automated (e.g. when offline)
|
||||
UpdateCheckType::Automatic => {
|
||||
log::info!("auto-update check failed: error:{:?}", error);
|
||||
@@ -715,11 +778,21 @@ impl AutoUpdater {
|
||||
}
|
||||
|
||||
fn check_dependencies() -> Result<()> {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[cfg(target_os = "linux")]
|
||||
if which::which("rsync").is_err() {
|
||||
let install_hint = linux_rsync_install_hint();
|
||||
return Err(MissingDependencyError(format!(
|
||||
"rsync is required for auto-updates but is not installed. {install_hint}"
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
anyhow::ensure!(
|
||||
which::which("rsync").is_ok(),
|
||||
"Could not auto-update because the required rsync utility was not found."
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user