Show real versions for PdfCraft and SoundCraft in the launcher. (#195)
Those binaries print a former id (`printcraft 0.2.1`) or the display name (`SoundCraft 0.5.0`). Detection required an exact machine-id prefix, so the version was missing and the card said "installed installed · latest …". Parse --version against stems and the product name, and stop using the word "installed" as a version placeholder.
This commit is contained in:
parent
80f70a1a33
commit
1f92b7db17
|
|
@ -86,6 +86,34 @@ pub(crate) fn appimage_version(name: &str, stems: &[&str]) -> Option<Option<Stri
|
|||
(parsed.kind == artcraft_launcher_core::PackageKind::AppImage && parsed.token.starts_with("linux-")).then_some(Some(parsed.version))
|
||||
}
|
||||
|
||||
/// Version from `<app> --version` output. Binaries print the machine id (`photocraft 0.7.0`),
|
||||
/// the display name (`SoundCraft 0.5.0`), or a former id (`printcraft 0.2.1` for PdfCraft).
|
||||
#[cfg_attr(not(any(target_os = "linux", target_os = "freebsd")), allow(dead_code))]
|
||||
fn parse_cli_version(line: &str, app: &CraftApp) -> Option<version::Version> {
|
||||
let line = line.trim();
|
||||
for prefix in app.stems().chain(std::iter::once(app.name)) {
|
||||
if let Some(rest) = strip_cli_prefix(line, prefix)
|
||||
&& let Some(tok) = rest.split_whitespace().next()
|
||||
&& let Some(v) = version::parse(tok)
|
||||
{
|
||||
return Some(v);
|
||||
}
|
||||
}
|
||||
version::parse_package(line)
|
||||
}
|
||||
|
||||
fn strip_cli_prefix<'a>(line: &'a str, prefix: &str) -> Option<&'a str> {
|
||||
let (head, rest) = line.split_at_checked(prefix.len())?;
|
||||
if !head.eq_ignore_ascii_case(prefix) {
|
||||
return None;
|
||||
}
|
||||
match rest.chars().next() {
|
||||
None => Some(rest),
|
||||
Some(c) if c.is_ascii_whitespace() => Some(rest),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod macos {
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -339,7 +367,7 @@ pub mod linux {
|
|||
/// to whatever installed it, since it can't tell which package manager that was.
|
||||
fn detect_system_install(app: &CraftApp) -> Option<Installed> {
|
||||
let bin = find_binary(app.id)?;
|
||||
Some(Installed { version: binary_version(&bin, app.id), source: InstallSource::Binary, location: bin.display().to_string(), package: None })
|
||||
Some(Installed { version: binary_version(&bin, app), source: InstallSource::Binary, location: bin.display().to_string(), package: None })
|
||||
}
|
||||
|
||||
fn find_binary(id: &str) -> Option<PathBuf> {
|
||||
|
|
@ -348,10 +376,9 @@ pub mod linux {
|
|||
on_path.or_else(|| usual.map(|d| d.join(id)).find(|p| p.is_file()))
|
||||
}
|
||||
|
||||
pub(crate) fn binary_version(bin: &Path, id: &str) -> Option<version::Version> {
|
||||
pub(crate) fn binary_version(bin: &Path, app: &CraftApp) -> Option<version::Version> {
|
||||
let out = crate::process::capture(bin.to_str()?, &["--version"], Duration::from_secs(5))?;
|
||||
let rest = out.trim().strip_prefix(id)?.trim_start();
|
||||
version::parse(rest.split_whitespace().next()?)
|
||||
parse_cli_version(out.trim(), app)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -367,14 +394,13 @@ pub mod freebsd {
|
|||
if !bin.exists() {
|
||||
return None;
|
||||
}
|
||||
Some(Installed { version: binary_version(&bin, app.id), source: InstallSource::Binary, location: bin.display().to_string(), package: None })
|
||||
Some(Installed { version: binary_version(&bin, app), source: InstallSource::Binary, location: bin.display().to_string(), package: None })
|
||||
}
|
||||
|
||||
/// `<app> --version` prints `<app> 0.5.0 (sha, date)`.
|
||||
fn binary_version(bin: &Path, id: &str) -> Option<version::Version> {
|
||||
fn binary_version(bin: &Path, app: &CraftApp) -> Option<version::Version> {
|
||||
let out = crate::process::capture(bin.to_str()?, &["--version"], Duration::from_secs(5))?;
|
||||
let rest = out.trim().strip_prefix(id)?.trim_start();
|
||||
version::parse(rest.split_whitespace().next()?)
|
||||
parse_cli_version(out.trim(), app)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -412,9 +438,25 @@ mod linux_tests {
|
|||
std::fs::write(&bin, "#!/bin/sh\necho 'photocraft 0.6.0 (0c72d9542, 2026-10-10)'\n").unwrap();
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
std::fs::set_permissions(&bin, std::fs::Permissions::from_mode(0o755)).unwrap();
|
||||
let ver = linux::binary_version(&bin, photo.id);
|
||||
let ver = linux::binary_version(&bin, photo);
|
||||
assert_eq!(ver.unwrap().to_string(), "0.6.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reads_binary_version_when_the_cli_uses_the_display_name_or_a_former_id() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let write = |name: &str, script: &str| {
|
||||
let bin = temp_dir.path().join(name);
|
||||
std::fs::write(&bin, script).unwrap();
|
||||
std::fs::set_permissions(&bin, std::fs::Permissions::from_mode(0o755)).unwrap();
|
||||
bin
|
||||
};
|
||||
let sound = write("soundcraft", "#!/bin/sh\necho 'SoundCraft 0.5.0'\n");
|
||||
assert_eq!(linux::binary_version(&sound, CraftApp::by_id("soundcraft").unwrap()).unwrap().to_string(), "0.5.0");
|
||||
let pdf = write("pdfcraft", "#!/bin/sh\necho 'printcraft 0.2.1'\n");
|
||||
assert_eq!(linux::binary_version(&pdf, CraftApp::by_id("pdfcraft").unwrap()).unwrap().to_string(), "0.2.1");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -446,6 +488,18 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_cli_version_accepts_id_display_name_and_former_id() {
|
||||
let photo = CraftApp::by_id("photocraft").unwrap();
|
||||
let sound = CraftApp::by_id("soundcraft").unwrap();
|
||||
let pdf = CraftApp::by_id("pdfcraft").unwrap();
|
||||
assert_eq!(parse_cli_version("photocraft 0.7.0 (dev build)", photo).unwrap().to_string(), "0.7.0");
|
||||
assert_eq!(parse_cli_version("SoundCraft 0.5.0", sound).unwrap().to_string(), "0.5.0");
|
||||
assert_eq!(parse_cli_version("printcraft 0.2.1", pdf).unwrap().to_string(), "0.2.1");
|
||||
assert_eq!(parse_cli_version("PdfCraft 0.6.0", pdf).unwrap().to_string(), "0.6.0");
|
||||
assert!(strip_cli_prefix("photocraft-cli 1.0.0", "photocraft").is_none());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn finds_appimages_in_every_folder_newest_first() {
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ month Dec Dez.
|
|||
{version} installed → {latest} released {version} installiert → {latest} veröffentlicht
|
||||
Version {version} Version {version}
|
||||
{installed} installed · latest {latest} {installed} installiert · neueste {latest}
|
||||
Installed · latest {latest} Installiert · neueste {latest}
|
||||
Version {version} installed Version {version} installiert
|
||||
Version {latest} Version {latest}
|
||||
No release published yet Noch keine Version veröffentlicht
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 11 and column 16.
|
|
|
@ -201,6 +201,7 @@ month Dec déc.
|
|||
{version} installed → {latest} released {version} installée → {latest} publiée
|
||||
Version {version} Version {version}
|
||||
{installed} installed · latest {latest} {installed} installée · dernière {latest}
|
||||
Installed · latest {latest} Installée · dernière {latest}
|
||||
Version {version} installed Version {version} installée
|
||||
Version {latest} Version {latest}
|
||||
No release published yet Aucune version publiée pour l'instant
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 5 and column 50.
|
|
|
@ -191,6 +191,7 @@ month Dec dez.
|
|||
{version} installed → {latest} released {version} instalado → {latest} lançado
|
||||
Version {version} Versão {version}
|
||||
{installed} installed · latest {latest} {installed} instalado · última {latest}
|
||||
Installed · latest {latest} Instalado · última {latest}
|
||||
Version {version} installed Versão {version} instalada
|
||||
Version {latest} Versão {latest}
|
||||
No release published yet Nenhum lançamento publicado ainda
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 9.
|
|
|
@ -201,6 +201,7 @@ month Dec дек.
|
|||
{version} installed → {latest} released {version} установлено → {latest} выпущено
|
||||
Version {version} Версия {version}
|
||||
{installed} installed · latest {latest} {installed} установлено · последняя {latest}
|
||||
Installed · latest {latest} Установлено · последняя {latest}
|
||||
Version {version} installed Версия {version} установлена
|
||||
Version {latest} Версия {latest}
|
||||
No release published yet Релиз ещё не опубликован
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 13 and column 44.
|
|
|
@ -126,6 +126,7 @@ month Dec 12月
|
|||
{version} installed → {latest} released 已安装 {version} → 最新 {latest}
|
||||
Version {version} 版本 {version}
|
||||
{installed} installed · latest {latest} 已安装 {installed} · 最新 {latest}
|
||||
Installed · latest {latest} 已安装 · 最新 {latest}
|
||||
Version {version} installed 已安装版本 {version}
|
||||
Version {latest} 版本 {latest}
|
||||
No release published yet 尚未发布版本
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 4 and column 3.
|
|
|
@ -57,7 +57,7 @@ impl AppEntry {
|
|||
}
|
||||
|
||||
pub fn installed_label(&self) -> Option<String> {
|
||||
self.installed.as_ref().map(|i| i.version.as_ref().map_or_else(|| t("installed").to_string(), ToString::to_string))
|
||||
self.installed.as_ref().map(|i| i.version.as_ref().map(ToString::to_string).unwrap_or_else(|| t("Installed").to_string()))
|
||||
}
|
||||
|
||||
pub fn latest_label(&self) -> Option<String> {
|
||||
|
|
|
|||
|
|
@ -847,14 +847,18 @@ fn sort_label(k: SortKey) -> &'static str {
|
|||
/// "0.4.1 installed → 0.5.0 released · Oct 8, 2026" and friends.
|
||||
fn version_line(e: &AppEntry, status: &AppStatus) -> String {
|
||||
let released = e.release().and_then(|r| r.published_at).map(|at| format!(" · {}", date_text(at))).unwrap_or_default();
|
||||
match (status, e.installed_label(), e.latest_label()) {
|
||||
let inst_ver = e.installed.as_ref().and_then(|i| i.version.as_ref().map(ToString::to_string));
|
||||
let latest = e.latest_label();
|
||||
match (status, inst_ver.as_deref(), latest.as_deref()) {
|
||||
(AppStatus::UpdateAvailable, Some(i), Some(l)) => {
|
||||
format!("{}{released}", fmt(tl!("{version} installed → {latest} released"), &[("version", i.as_str()), ("latest", l.as_str())]))
|
||||
format!("{}{released}", fmt(tl!("{version} installed → {latest} released"), &[("version", i), ("latest", l)]))
|
||||
}
|
||||
(AppStatus::UpToDate, Some(i), _) => format!("{}{released}", fmt(tl!("Version {version}"), &[("version", i.as_str())])),
|
||||
(_, Some(i), Some(l)) => fmt(tl!("{installed} installed · latest {latest}"), &[("installed", i.as_str()), ("latest", l.as_str())]),
|
||||
(_, Some(i), None) => fmt(tl!("Version {version} installed"), &[("version", i.as_str())]),
|
||||
(_, None, Some(l)) => format!("{}{released}", fmt(tl!("Version {latest}"), &[("latest", l.as_str())])),
|
||||
(AppStatus::UpToDate, Some(i), _) => format!("{}{released}", fmt(tl!("Version {version}"), &[("version", i)])),
|
||||
(_, Some(i), Some(l)) => fmt(tl!("{installed} installed · latest {latest}"), &[("installed", i), ("latest", l)]),
|
||||
(_, None, Some(l)) if e.installed.is_some() => fmt(tl!("Installed · latest {latest}"), &[("latest", l)]),
|
||||
(_, Some(i), None) => fmt(tl!("Version {version} installed"), &[("version", i)]),
|
||||
(_, None, None) if e.installed.is_some() => tl!("Installed").to_string(),
|
||||
(_, None, Some(l)) => format!("{}{released}", fmt(tl!("Version {latest}"), &[("latest", l)])),
|
||||
(AppStatus::NoRelease, None, None) => tl!("No release published yet").to_string(),
|
||||
_ => " ".to_string(),
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue