mirror of
https://github.com/storytold/craft-launcher.git
synced 2026-10-14 14:26:48 -04:00
Fixed debian installs being flagged as "Update available" when updated to latest version
Rebased onto main and combined with 8143a26: keeps the -bin/-git AUR detection and pre-release-aware version parsing from main, adds this change's pacman removal (now using the detected package name), its X.Y.Z-anywhere fallback and its dialog wording.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8143a26014
commit
37a1ef72ad
4 files changed
+65
-20
No files matched your search
@@ -3,6 +3,8 @@ plan/
|
||||
plans/
|
||||
/target
|
||||
target/
|
||||
/builds
|
||||
builds/
|
||||
/dist
|
||||
/temp
|
||||
/craft-fonts
|
||||
@@ -57,10 +57,46 @@ pub fn parse_package(text: &str) -> Option<Version> {
|
||||
match parts.as_slice() {
|
||||
[a, b, c] => Version::parse(&format!("{a}.{b}.{c}")).ok(),
|
||||
[a, b] => Version::parse(&format!("{a}.{b}.0")).ok(),
|
||||
_ => None,
|
||||
// Text around the version, as in `--version` output (`PhotoCraft 0.5.0 (abc, 2026)`):
|
||||
// the first `X.Y.Z` run anywhere.
|
||||
_ => first_xyz(&t),
|
||||
}
|
||||
}
|
||||
|
||||
/// The first `major.minor.patch` run anywhere in `text`.
|
||||
fn first_xyz(text: &str) -> Option<Version> {
|
||||
let bytes = text.as_bytes();
|
||||
for start in 0..bytes.len() {
|
||||
if !bytes.get(start).is_some_and(u8::is_ascii_digit) || (start > 0 && bytes.get(start - 1).is_some_and(u8::is_ascii_digit)) {
|
||||
continue;
|
||||
}
|
||||
let Some((major, i)) = digits(text, start) else { continue };
|
||||
if bytes.get(i) != Some(&b'.') {
|
||||
continue;
|
||||
}
|
||||
let Some((minor, j)) = digits(text, i + 1) else { continue };
|
||||
if bytes.get(j) != Some(&b'.') {
|
||||
continue;
|
||||
}
|
||||
let Some((patch, _)) = digits(text, j + 1) else { continue };
|
||||
return Some(Version::new(major, minor, patch));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// A run of ASCII digits from `from`, as a number and the index just past it.
|
||||
fn digits(text: &str, from: usize) -> Option<(u64, usize)> {
|
||||
let bytes = text.as_bytes();
|
||||
let mut end = from;
|
||||
while bytes.get(end).is_some_and(u8::is_ascii_digit) {
|
||||
end += 1;
|
||||
}
|
||||
if end == from {
|
||||
return None;
|
||||
}
|
||||
text.get(from..end)?.parse().ok().map(|n| (n, end))
|
||||
}
|
||||
|
||||
/// A version that's a plain `X.Y.Z` with no pre-release suffix.
|
||||
pub fn is_stable(v: &Version) -> bool {
|
||||
v.pre.is_empty()
|
||||
@@ -122,6 +158,10 @@ mod tests {
|
||||
assert_eq!(p("0.3.0.r47.g47f9306-1").as_deref(), Some("0.3.0"));
|
||||
// Plain versions (rpm %{VERSION}, Flatpak) pass through.
|
||||
assert_eq!(p("0.4.0").as_deref(), Some("0.4.0"));
|
||||
assert_eq!(p("0.5").as_deref(), Some("0.5.0"));
|
||||
// A version inside text, as `--version` prints it.
|
||||
assert_eq!(p("PhotoCraft 0.5.0 (abc, 2026)").as_deref(), Some("0.5.0"));
|
||||
assert_eq!(p("junk"), None);
|
||||
assert_eq!(p("garbage"), None);
|
||||
// The bug this guards: 0.5.0-1 used to read as a pre-release, so an installed .deb of the
|
||||
// latest release always looked out of date.
|
||||
|
||||
@@ -93,11 +93,12 @@ pub fn choose_kinds(existing: Option<&Installed>, record: Option<&Record>, prefe
|
||||
InstallSource::Msi => Some(PackageKind::Msi),
|
||||
InstallSource::Deb => Some(PackageKind::Deb),
|
||||
InstallSource::Rpm => Some(PackageKind::Rpm),
|
||||
// Arch/AUR installs are owned by pacman and updated by the user's AUR helper (see
|
||||
// `managed_elsewhere`); the launcher opens and removes them but never replaces them.
|
||||
InstallSource::Pacman => None,
|
||||
InstallSource::Flatpak => Some(PackageKind::Flatpak),
|
||||
InstallSource::AppImage => Some(PackageKind::AppImage),
|
||||
InstallSource::Binary => Some(PackageKind::TarGz),
|
||||
// Updated by the user's AUR helper; see `managed_elsewhere`.
|
||||
InstallSource::Pacman => None,
|
||||
};
|
||||
if let Some(k) = kind.filter(|k| available.contains(k)) {
|
||||
return vec![k];
|
||||
@@ -161,22 +162,20 @@ fn published_checksum(release: &Release, asset: &Asset, http: &Http) -> Result<O
|
||||
Ok(release::parse_sha256sums(&text).into_iter().find(|(_, n)| *n == asset.name).map(|(h, _)| h))
|
||||
}
|
||||
|
||||
/// When `installed` is owned by a package manager the launcher doesn't drive (the AUR), what to
|
||||
/// tell the user instead of installing a second copy.
|
||||
/// When `installed` is owned by something the launcher doesn't drive, what to tell the user
|
||||
/// instead of installing a second copy. AUR packages are removed through pacman like any system
|
||||
/// package, but updated by the user's AUR helper, which builds as the user and can't run behind
|
||||
/// the password prompt.
|
||||
pub fn managed_elsewhere(app: &CraftApp, installed: &Installed, uninstall: bool) -> Option<String> {
|
||||
match installed.source {
|
||||
InstallSource::Pacman => {
|
||||
InstallSource::Pacman if !uninstall => {
|
||||
let pkg = installed.package.clone().unwrap_or_else(|| app.id.to_string());
|
||||
Some(if uninstall {
|
||||
format!("{} was installed from the AUR ({pkg}). Remove it with: sudo pacman -R {pkg}", app.name)
|
||||
} else {
|
||||
let helper = ["paru", "yay"].into_iter().find(|h| which(h).is_some());
|
||||
let cmd = match helper {
|
||||
Some(h) => format!("{h} -S {pkg}"),
|
||||
None => format!("your AUR helper (for example: yay -S {pkg})"),
|
||||
};
|
||||
format!("{} was installed from the AUR ({pkg}). Update it with {cmd}", app.name)
|
||||
})
|
||||
Some(format!("{} was installed from the AUR ({pkg}). Update it with {cmd}", app.name))
|
||||
}
|
||||
InstallSource::Binary => Some(format!("{} was installed outside the launcher. Update or remove it the way it was installed.", app.name)),
|
||||
_ => None,
|
||||
@@ -201,7 +200,7 @@ pub fn uninstall(app: &CraftApp, installed: &Installed, record: Option<&Record>)
|
||||
#[cfg(target_os = "macos")]
|
||||
InstallSource::MacApp | InstallSource::Launcher => macos::move_to_trash(&path),
|
||||
#[cfg(target_os = "linux")]
|
||||
InstallSource::Deb | InstallSource::Rpm | InstallSource::Flatpak => linux::remove_package(app, installed.source),
|
||||
InstallSource::Deb | InstallSource::Rpm | InstallSource::Pacman | InstallSource::Flatpak => linux::remove_package(app, installed),
|
||||
#[cfg(target_os = "freebsd")]
|
||||
InstallSource::Launcher => match record {
|
||||
Some(r) => freebsd::remove_files(&r.files, r.needs_root),
|
||||
@@ -373,8 +372,8 @@ mod linux {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_package(app: &CraftApp, source: InstallSource) -> Result<(), String> {
|
||||
match source {
|
||||
pub fn remove_package(app: &CraftApp, installed: &Installed) -> Result<(), String> {
|
||||
match installed.source {
|
||||
InstallSource::Deb => run_as_root(&[OsStr::new("apt-get"), OsStr::new("remove"), OsStr::new("-y"), OsStr::new(app.id)]),
|
||||
InstallSource::Rpm => {
|
||||
if which("dnf").is_some() {
|
||||
@@ -383,6 +382,11 @@ mod linux {
|
||||
run_as_root(&[OsStr::new("rpm"), OsStr::new("-e"), OsStr::new(app.id)])
|
||||
}
|
||||
}
|
||||
// The package detected (photocraft, photocraft-bin or photocraft-git).
|
||||
InstallSource::Pacman => {
|
||||
let pkg = installed.package.as_deref().unwrap_or(app.id);
|
||||
run_as_root(&[OsStr::new("pacman"), OsStr::new("-R"), OsStr::new("--noconfirm"), OsStr::new(pkg)])
|
||||
}
|
||||
InstallSource::Flatpak => {
|
||||
let id = app.bundle_id();
|
||||
let user = process::command("flatpak").args(["uninstall", "--user", "-y", "--noninteractive", &id]).status();
|
||||
|
||||
@@ -80,12 +80,11 @@ impl LauncherUi {
|
||||
Some(artcraft_launcher_core::InstallSource::Msi) => {
|
||||
"Windows will remove it like any installed program. Your documents and settings stay."
|
||||
}
|
||||
Some(artcraft_launcher_core::InstallSource::Deb | artcraft_launcher_core::InstallSource::Rpm) => {
|
||||
"Your system's package manager will remove it, and may ask for your password. Your documents and settings stay."
|
||||
}
|
||||
Some(artcraft_launcher_core::InstallSource::Pacman) => {
|
||||
"It was installed from the AUR, so the launcher will show you the pacman command to remove it."
|
||||
}
|
||||
Some(
|
||||
artcraft_launcher_core::InstallSource::Deb
|
||||
| artcraft_launcher_core::InstallSource::Rpm
|
||||
| artcraft_launcher_core::InstallSource::Pacman,
|
||||
) => "Your system's package manager will remove it, and may ask for your password. Your documents and settings stay.",
|
||||
_ => "The app will be removed. Your documents and settings stay.",
|
||||
};
|
||||
ui.label(RichText::new(how).color(t.text_muted));
|
||||
|
||||
Reference in new issue
Block a user