Merge pull request #1882 from rmcrackan/rmcrackan/1881-linux-upgrade

#1881 - Fix Linux in-app upgrades
This commit is contained in:
rmcrackan
2026-06-17 15:51:26 -04:00
committed by GitHub
2 changed files with 54 additions and 27 deletions

View File

@@ -349,7 +349,7 @@ public static class LibationScaffolding
private static void wireUpSystemEvents(Configuration configuration)
{
LibraryCommands.LibrarySizeChanged += (object? _, List<DataLayer.LibraryBook> libraryBooks)
LibraryCommands.LibrarySizeChanged += (_, libraryBooks)
=> SearchEngineCommands.FullReIndex(libraryBooks);
LibraryCommands.BookUserDefinedItemCommitted += (_, books)
@@ -423,26 +423,38 @@ public static class LibationScaffolding
var releaseIndex = JObject.Parse(System.Text.Encoding.ASCII.GetString(bts));
string? regexPattern;
string? releaseIdString = null;
try
{
regexPattern = releaseIndex.Value<string>(InteropFactory.Create().ReleaseIdString);
releaseIdString = InteropFactory.Create().ReleaseIdString;
regexPattern = releaseIndex.Value<string>(releaseIdString);
}
catch
{
regexPattern = null;
}
if (string.IsNullOrEmpty(regexPattern) && Configuration.IsLinux)
{
var baseId = ReleaseIdentifier.ToString();
regexPattern = releaseIndex.Value<string>($"{baseId}_RPM")
?? releaseIndex.Value<string>($"{baseId}_DEB");
releaseIdString ??= $"{baseId}_RPM";
}
if (string.IsNullOrEmpty(regexPattern))
regexPattern = releaseIndex.Value<string>(ReleaseIdentifier.ToString());
if (string.IsNullOrEmpty(regexPattern))
{
Log.Logger.Warning("Release index has no entry for this platform (ReleaseIdentifier: {ReleaseId}). Version check inconclusive.", ReleaseIdentifier);
Log.Logger.Warning("Release index has no entry for this platform (ReleaseIdentifier: {ReleaseId}, ReleaseIdString: {ReleaseIdString}). Version check inconclusive.", ReleaseIdentifier, releaseIdString);
return (null, null, null, false);
}
var regex = new System.Text.RegularExpressions.Regex(regexPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
var zip = latestRelease?.Assets?.FirstOrDefault(a => regex.IsMatch(a.Name));
if (zip is not null && !string.IsNullOrEmpty(releaseIdString))
Log.Logger.Information("Update asset matched using {ReleaseIdString}: {AssetName}", releaseIdString, zip.Name);
return (releaseVersion, latestRelease, zip, true);
}
}
@@ -499,7 +511,7 @@ internal static class Migrations
class FilterState_6_6_9
{
public bool UseDefault { get; set; }
public List<string> Filters { get; set; } = new();
public List<string> Filters { get; set; } = [];
}
public static void migrate_to_v12_0_1(Configuration config)
@@ -520,7 +532,7 @@ internal static class Migrations
if (JArray.Parse(File.ReadAllText(jsonFileV1)) is not JArray v1Cache || v1Cache.Count == 0)
return;
Dictionary<string, JArray> cache = new();
Dictionary<string, JArray> cache = [];
//Convert to c# objects to speed up searching by ID inside the iterator
var allItems
@@ -612,10 +624,12 @@ internal static class Migrations
if (JsonConvert.DeserializeObject<FilterState_6_6_9>(File.ReadAllText(QuickFilters.JsonFile))
is FilterState_6_6_9 inMemState)
{
// Copy old structure to new.
QuickFilters.InMemoryState = new();
QuickFilters.InMemoryState.UseDefault = inMemState.UseDefault;
foreach (var oldFilter in inMemState.Filters)
// Copy old structure to new.
QuickFilters.InMemoryState = new()
{
UseDefault = inMemState.UseDefault
};
foreach (var oldFilter in inMemState.Filters)
QuickFilters.InMemoryState.Filters.Add(new QuickFilters.NamedFilter(oldFilter, null));
return;

View File

@@ -27,7 +27,7 @@ internal class LinuxInterop : IInteropFunctions
public void SetFolderIcon(byte[] imageJpegBytes, string directory) => throw new PlatformNotSupportedException();
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
public string ReleaseIdString => LibationScaffolding.ReleaseIdentifier.ToString() + (File.Exists("/usr/bin/apt") || File.Exists("/bin/apt") ? "_DEB" : "_RPM");
public string ReleaseIdString => LibationScaffolding.ReleaseIdentifier.ToString() + LinuxPackageFormatSuffix();
//only run the auto upgrader if the current app was installed from the
//.deb or .rpm package. Try to detect this by checking if the symlink exists.
@@ -73,31 +73,44 @@ internal class LinuxInterop : IInteropFunctions
private static bool TryResolvePackageManager(string upgradeBundle, out string pkgExe, out string[] pkgArgs)
{
if (TryFirstExisting(out pkgExe, "/usr/bin/dnf5", "/bin/dnf5"))
var isRpmBundle = string.Equals(Path.GetExtension(upgradeBundle), ".rpm", StringComparison.OrdinalIgnoreCase);
var isDebBundle = string.Equals(Path.GetExtension(upgradeBundle), ".deb", StringComparison.OrdinalIgnoreCase);
if (isRpmBundle || (!isDebBundle && UsesRpmPackageFormat()))
{
pkgArgs = new[] { "install", "-y", upgradeBundle };
return true;
if (TryFirstExisting(out pkgExe, "/usr/bin/dnf5", "/bin/dnf5")
|| TryFirstExisting(out pkgExe, "/usr/bin/dnf", "/bin/dnf")
|| TryFirstExisting(out pkgExe, "/usr/bin/yum", "/bin/yum"))
{
pkgArgs = new[] { "install", "-y", upgradeBundle };
return true;
}
}
if (TryFirstExisting(out pkgExe, "/usr/bin/dnf", "/bin/dnf"))
if (isDebBundle || UsesDebPackageFormat())
{
pkgArgs = new[] { "install", "-y", upgradeBundle };
return true;
}
if (TryFirstExisting(out pkgExe, "/usr/bin/yum", "/bin/yum"))
{
pkgArgs = new[] { "install", "-y", upgradeBundle };
return true;
}
if (TryFirstExisting(out pkgExe, "/usr/bin/apt", "/bin/apt"))
{
pkgArgs = new[] { "install", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", upgradeBundle };
return true;
if (TryFirstExisting(out pkgExe, "/usr/bin/apt", "/bin/apt"))
{
pkgArgs = new[] { "install", "-y", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", upgradeBundle };
return true;
}
}
pkgExe = "";
pkgArgs = Array.Empty<string>();
return false;
}
// RHEL/Fedora can have apt installed for .deb files; prefer native RPM managers when present.
internal static string LinuxPackageFormatSuffix()
=> UsesRpmPackageFormat() ? "_RPM" : UsesDebPackageFormat() ? "_DEB" : "_RPM";
private static bool UsesRpmPackageFormat()
=> TryFirstExisting(out _, "/usr/bin/dnf5", "/bin/dnf5", "/usr/bin/dnf", "/bin/dnf", "/usr/bin/yum", "/bin/yum");
private static bool UsesDebPackageFormat()
=> TryFirstExisting(out _, "/usr/bin/apt", "/bin/apt");
private static bool TryFirstExisting(out string path, params string[] candidates)
{
foreach (var c in candidates)
@@ -150,7 +163,7 @@ internal class LinuxInterop : IInteropFunctions
console[2],
"/bin/sh",
Path.Combine(Configuration.ProcessDirectory, runasroot), //script file
"Installing libation.deb", //command title
"Installing Libation package", //command title
command, // command to execute vis /bin/sh
$"Please run '{command}' manually" // error message to display in the terminal
}