Compare commits

..
Author SHA1 Message Date
Lotus CIandClaude Opus 5.5 261d7852cb fix(updater): package installs update through their package manager
On CachyOS the in-app update failed with "Permission denied (os error 13)
at path /usr/bin/tauri_current_app…": the app was installed from the
Arch package, and Tauri's Linux updater can only replace an AppImage — for
anything else it tries to write next to the binary in /usr/bin.

- update_install_kind command: "in-app" on Windows and for an AppImage
  ($APPIMAGE set); on Linux package installs "pacman" (ID/ID_LIKE arch:
  Arch, CachyOS, Manjaro, EndeavourOS…), "deb" (debian/ubuntu and
  derivatives) or "manual". The web UI shows the matching update command.
- install_update refuses up front on a package install
  ("install: package-managed (…)") instead of downloading the whole
  update and failing at the last step.

Tests: CachyOS/Arch → pacman; Ubuntu/Debian/Mint → deb; Fedora/unknown →
manual; AppImage and Windows → in-app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-30 18:46:11 -04:00
jared 06fe43c3cd Merge pull request 'Windows smoke: foreign-page mic check on a local origin (#19)' (#31) from smoke-foreign-origin into main
Build Lotus Chat Desktop / prepare (push) Successful in 2s
Build Lotus Chat Desktop / build-linux (push) Successful in 24m28s
Build Lotus Chat Desktop / build-arch (push) Successful in 14s
Build Lotus Chat Desktop / build-windows (push) Successful in 26m6s
Build Lotus Chat Desktop / update-manifest (push) Successful in 6s
Merge pull request #31: smoke foreign-page check (#19)
2026-09-30 12:46:31 -04:00
+81
View File
@@ -189,6 +189,47 @@ mod update_retry {
}
}
/// How this copy of the app gets updated.
///
/// Tauri's updater can replace the Windows install and a Linux AppImage, but
/// not a copy installed by a package manager: it tries to write next to the
/// binary in /usr/bin and fails with "Permission denied (os error 13)"
/// (reported on CachyOS). Those installs update through their package
/// manager instead; the web UI shows the right command.
pub(crate) fn install_kind(linux: bool, appimage: bool, os_release: &str) -> &'static str {
if !linux || appimage {
return "in-app";
}
let field = |key: &str| {
os_release
.lines()
.find_map(|l| l.strip_prefix(key).and_then(|v| v.strip_prefix('=')))
.map(|v| v.trim().trim_matches('"').to_ascii_lowercase())
.unwrap_or_default()
};
let ids = format!("{} {}", field("ID"), field("ID_LIKE"));
let has = |name: &str| ids.split_whitespace().any(|w| w == name);
if has("arch") {
"pacman"
} else if has("debian") || has("ubuntu") {
"deb"
} else {
"manual"
}
}
#[tauri::command]
fn update_install_kind() -> &'static str {
let linux = cfg!(target_os = "linux");
let appimage = std::env::var_os("APPIMAGE").is_some();
let os_release = if linux {
std::fs::read_to_string("/etc/os-release").unwrap_or_default()
} else {
String::new()
};
install_kind(linux, appimage, &os_release)
}
#[tauri::command]
async fn check_for_update(app: tauri::AppHandle) -> Result<UpdateInfo, String> {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -211,6 +252,15 @@ async fn install_update(app: tauri::AppHandle) -> Result<(), String> {
{
use std::time::{Duration, Instant};
// A package-manager install can't be replaced in place (see
// install_kind); refuse before downloading anything.
let kind = update_install_kind();
if kind != "in-app" {
return Err(format!(
"install: package-managed ({kind}): update Lotus Chat with your package manager"
));
}
let emit = |detail: serde_json::Value| {
native::emit_to_web(&app, "lotus-update-progress", &detail.to_string());
};
@@ -939,6 +989,7 @@ pub fn run() {
send_notification,
check_for_update,
install_update,
update_install_kind,
native::power::set_call_active,
native::jumplist::set_jump_list,
native::thumbbar::set_thumbbar,
@@ -1333,6 +1384,36 @@ mod webview2_args_tests {
}
}
#[cfg(test)]
mod install_kind_tests {
use super::install_kind;
#[test]
fn package_installs_update_through_their_package_manager() {
let cachy = "NAME=\"CachyOS Linux\"\nID=cachyos\nID_LIKE=arch\n";
let arch = "NAME=\"Arch Linux\"\nID=arch\n";
let ubuntu = "NAME=\"Ubuntu\"\nID=ubuntu\nID_LIKE=debian\n";
let debian = "ID=debian\n";
let mint = "ID=linuxmint\nID_LIKE=\"ubuntu debian\"\n";
let fedora = "ID=fedora\n";
assert_eq!(install_kind(true, false, cachy), "pacman");
assert_eq!(install_kind(true, false, arch), "pacman");
assert_eq!(install_kind(true, false, ubuntu), "deb");
assert_eq!(install_kind(true, false, debian), "deb");
assert_eq!(install_kind(true, false, mint), "deb");
assert_eq!(install_kind(true, false, fedora), "manual");
assert_eq!(install_kind(true, false, ""), "manual");
// ID_LIKE mentioning arch only as part of a longer word doesn't count.
assert_eq!(install_kind(true, false, "ID=x\nID_LIKE=archlike\n"), "manual");
}
#[test]
fn appimage_and_windows_update_in_app() {
assert_eq!(install_kind(true, true, "ID=cachyos\nID_LIKE=arch\n"), "in-app");
assert_eq!(install_kind(false, false, ""), "in-app");
}
}
#[cfg(test)]
mod tray_tests {
use super::*;