(Improvement) Auto-upgrade procedure for non-Windows systems

This commit is contained in:
Tiago Conceição
2022-10-23 22:19:38 +01:00
parent ab11d40d33
commit 18b208bf31
7 changed files with 188 additions and 92 deletions
+2 -2
View File
@@ -907,7 +907,7 @@ public class GCodeBuilder : BindableBase
var layerBlock = new GCodeLayer(slicerFile);
bool parseLayerIndexFromComments = false;
//bool parseLayerIndexFromComments = false;
using var reader = new StringReader(gcode);
string? line;
@@ -945,7 +945,7 @@ public class GCodeBuilder : BindableBase
}
layerBlock.SetLayer(true);
layerBlock.LayerIndex = layerIndex;
parseLayerIndexFromComments = true;
//parseLayerIndexFromComments = true;
continue;
}
}
+44
View File
@@ -0,0 +1,44 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.IO;
namespace UVtools.Core.SystemOS;
/// <summary>
/// Linux specific methods
/// </summary>
public static class Linux
{
/// <summary>
/// Gets if is running under Linux and under AppImage format
/// </summary>
public static bool IsRunningAppImage => !string.IsNullOrWhiteSpace(RunningAppImageRootPath);
/// <summary>
/// Gets if is running under Linux and under AppImage format and return the full root path for the running AppImage
/// </summary>
public static bool IsRunningAppImageGetPath(out string? path)
{
path = RunningAppImageRootPath;
return !string.IsNullOrWhiteSpace(path);
}
/// <summary>
/// <para>Gets the full root path for the running AppImage. Returns null is not Linux and null/empty if not an AppImage</para>
/// <para>The return path is the source file location and not the execution path location.</para>
/// </summary>
public static string? RunningAppImageRootPath => OperatingSystem.IsLinux() ? Environment.GetEnvironmentVariable("APPIMAGE") : null;
/// <summary>
/// Gets the name of the running *.app. Returns null or empty if not running an macOS .app. Returns null or empty if not an AppImage
/// </summary>
public static string? RunningAppName => Path.GetFileName(RunningAppImageRootPath);
}
+4 -14
View File
@@ -360,27 +360,17 @@ public static class SystemAware
return stringBuilder.ToString();
}
/// <summary>
/// Gets if is running under Linux and under AppImage format
/// </summary>
public static bool IsRunningLinuxAppImage(out string? path)
{
path = null;
if (!OperatingSystem.IsLinux()) return false;
path = Environment.GetEnvironmentVariable("APPIMAGE");
return !string.IsNullOrWhiteSpace(path);
}
/// <summary>
/// Gets if is running under Linux and under AppImage format
/// </summary>
/// <returns></returns>
public static bool IsRunningLinuxAppImage() => IsRunningLinuxAppImage(out _);
public static bool IsRunningLinuxAppImage => Linux.IsRunningAppImage;
/// <summary>
/// Gets if is running under MacOS and under app format
/// Gets if is running under MacOS and under *.app format
/// </summary>
public static bool IsRunningMacOSApp => OperatingSystem.IsMacOS() && AppContext.BaseDirectory.EndsWith(Path.Combine(".app", "Contents", $"MacOS{Path.DirectorySeparatorChar}"));
public static bool IsRunningMacOSApp => macOS.IsRunningApp;
/// <summary>
/// Gets the main name of the operative system
+43
View File
@@ -0,0 +1,43 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.IO;
namespace UVtools.Core.SystemOS;
/// <summary>
/// MacOS specific methods
/// </summary>
public static class macOS
{
/// <summary>
/// Gets if is running under MacOS and under .app format
/// </summary>
public static bool IsRunningApp => OperatingSystem.IsMacOS() && AppContext.BaseDirectory.EndsWith(Path.Combine(".app", "Contents", $"MacOS{Path.DirectorySeparatorChar}"));
/// <summary>
/// Gets if is running under macOS and under .app format and return the full root path for the running .app
/// </summary>
public static bool IsRunningAppGetPath(out string? path)
{
path = RunningAppRootPath;
return !string.IsNullOrWhiteSpace(path);
}
/// <summary>
/// Gets the full root path for the running .app. Returns null or empty if not running an macOS .app
/// </summary>
public static string? RunningAppRootPath => IsRunningApp ? Directory.GetParent(AppContext.BaseDirectory)?.Parent?.Parent?.FullName : null;
/// <summary>
/// Gets the name of the running .app. Returns null or empty if not running an macOS .app
/// </summary>
public static string? RunningAppName => IsRunningApp ? Directory.GetParent(AppContext.BaseDirectory)?.Parent?.Parent?.Name : null;
}
+5
View File
@@ -245,6 +245,11 @@
IsVisible="{Binding IsDebug}"
Command="{Binding MenuHelpDebugLongMessageBoxClicked}"
i:MenuItem.Icon="fa-solid fa-message"/>
<MenuItem Header="Trigger new update"
IsVisible="{Binding IsDebug}"
Command="{Binding MenuHelpDebugTriggerNewUpdateClicked}"
i:MenuItem.Icon="fa-solid fa-cloud-arrow-up"/>
</MenuItem>
+6 -1
View File
@@ -755,7 +755,7 @@ public partial class MainWindow : WindowEx
if (Settings.General.CheckForUpdatesOnStartup)
{
Task.Factory.StartNew(VersionChecker.Check);
Task.Factory.StartNew(() => VersionChecker.Check());
}
ProcessFiles(Program.Args);
@@ -1186,6 +1186,11 @@ public partial class MainWindow : WindowEx
await this.MessageBoxError(string.Concat(Enumerable.Repeat("Informative message:\n\nLorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n", 100)));
}
public void MenuHelpDebugTriggerNewUpdateClicked()
{
VersionChecker.Check(true);
}
public async void MenuNewVersionClicked()
{
if (string.IsNullOrWhiteSpace(VersionChecker.DownloadLink))
+84 -75
View File
@@ -13,11 +13,8 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Avalonia.Threading;
using UVtools.Core;
using UVtools.Core.Extensions;
using UVtools.Core.Objects;
@@ -47,7 +44,7 @@ public class AppVersionChecker : BindableBase
if (!string.IsNullOrWhiteSpace(package) && (package.EndsWith("-x64") || package.EndsWith("-arm64")))
{
if (OperatingSystem.IsWindows()) return $"{About.Software}_{package}_v{_version}.msi";
if (SystemAware.IsRunningLinuxAppImage()) return $"{About.Software}_{package}_v{_version}.AppImage";
if (Linux.IsRunningAppImage) return $"{About.Software}_{package}_v{_version}.AppImage";
return $"{About.Software}_{package}_v{_version}.zip";
}
}
@@ -63,7 +60,7 @@ public class AppVersionChecker : BindableBase
}
if (OperatingSystem.IsLinux())
{
return SystemAware.IsRunningLinuxAppImage()
return Linux.IsRunningAppImage
? $"{About.Software}_linux-x64_v{_version}.AppImage"
: $"{About.Software}_linux-x64_v{_version}.zip";
}
@@ -126,7 +123,12 @@ public class AppVersionChecker : BindableBase
public string DownloadedFile { get; private set; }
public bool Check()
/// <summary>
/// Check for new version
/// </summary>
/// <param name="alwaysTrigger">True to always found as new update, otherwise it will compare versions and trigger only if a new version is found</param>
/// <returns></returns>
public bool Check(bool alwaysTrigger = false)
{
try
{
@@ -149,7 +151,7 @@ public class AppVersionChecker : BindableBase
Debug.WriteLine($"Version checker: v{About.VersionStr} <=> v{tag_name}");
Version checkVersion = new(tag_name);
Changelog = json["body"]?.ToString();
if (About.Version.CompareTo(checkVersion) < 0)
if (alwaysTrigger || About.Version.CompareTo(checkVersion) < 0)
{
var assets = json["assets"].AsArray();
@@ -201,78 +203,85 @@ public class AppVersionChecker : BindableBase
if (OperatingSystem.IsWindows())
{
SystemAware.StartProcess(DownloadedFile);
Environment.Exit(0);
}
else
else if (downloadFilename.EndsWith(".AppImage") && Linux.IsRunningAppImageGetPath(out var appImagePath)) // Linux AppImage
{
// Linux AppImage
if (downloadFilename.EndsWith(".AppImage") && SystemAware.IsRunningLinuxAppImage(out var appImagePath))
{
var directory = Path.GetDirectoryName(appImagePath);
//var oldFileName = Path.GetFileName(appImagePath);
// Try to keep same filename logic if user renamed the file, like UVtools.AppImage would keep same same
//var newFilename = Regex.Replace(oldFileName, @"v\d+.\d+.\d+", $"v{_version}");
var newFullPath = Path.Combine(directory, downloadFilename);
var directory = Path.GetDirectoryName(appImagePath);
//var oldFileName = Path.GetFileName(appImagePath);
// Try to keep same filename logic if user renamed the file, like UVtools.AppImage would keep same same
//var newFilename = Regex.Replace(oldFileName, @"v\d+.\d+.\d+", $"v{_version}");
var newFullPath = Path.Combine(directory, downloadFilename);
if (File.Exists(appImagePath)) File.Delete(appImagePath);
File.Move(DownloadedFile, newFullPath, true);
SystemAware.StartProcess("chmod", $"a+x \"{newFullPath}\"", true);
Thread.Sleep(500);
SystemAware.StartProcess(newFullPath);
}
else // MacOS and generic linux (no AppImage)
{
var upgradeFolder = "UPDATED_VERSION";
var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder);
await using (var stream = File.Open(DownloadedFile, FileMode.Open))
{
using ZipArchive zip = new(stream, ZipArchiveMode.Read);
zip.ExtractToDirectory(targetDir, true);
}
File.Delete(DownloadedFile);
var upgradeFileName = $"{About.Software}_upgrade.sh";
var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName);
await using (var stream = File.CreateText(upgradeFile))
{
stream.NewLine = "\n";
await stream.WriteLineAsync("#!/bin/bash");
await stream.WriteLineAsync($"echo {About.Software} v{About.Version} updater script");
await stream.WriteLineAsync($"cd '{App.ApplicationPath}'");
await stream.WriteLineAsync($"killall {About.Software}");
await stream.WriteLineAsync("sleep 1");
//stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &");
if (SystemAware.IsRunningMacOSApp)
{
await stream.WriteLineAsync($"find '{upgradeFolder}' -print0 | xargs -0 xattr -d com.apple.quarantine");
await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ../../../");
await stream.WriteLineAsync($"open ../../../{About.Software}.app");
}
else // Linux generic
{
await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* .");
await stream.WriteLineAsync($"if [ -f '{About.Software}' ]; then");
await stream.WriteLineAsync($" ./{About.Software} &");
await stream.WriteLineAsync("else");
await stream.WriteLineAsync($" dotnet {About.Software}.dll &");
await stream.WriteLineAsync("fi");
}
await stream.WriteLineAsync($"rm -fr {upgradeFolder}");
await stream.WriteLineAsync("sleep 0.5");
await stream.WriteLineAsync($"rm -f {upgradeFileName}");
//stream.WriteLine("exit");
}
SystemAware.StartProcess("bash", $"\"{upgradeFile}\"");
//App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
}
Environment.Exit(0);
if (File.Exists(appImagePath)) File.Delete(appImagePath);
File.Move(DownloadedFile, newFullPath, true);
SystemAware.StartProcess("chmod", $"a+x \"{newFullPath}\"", true);
Thread.Sleep(500);
SystemAware.StartProcess(newFullPath);
}
else // MacOS and generic linux (no AppImage -- plain zip)
{
var tmpDirectory = PathExtensions.GetTemporaryDirectory("UVtoolsUpdate-", true);
var extractDirectoryPath = Path.Combine(tmpDirectory, "extracted");
ZipFile.ExtractToDirectory(DownloadedFile, extractDirectoryPath, true);
File.Delete(DownloadedFile);
var upgradeScriptFileName = "upgrade.sh";
var upgradeScriptFilePath = Path.Combine(tmpDirectory, upgradeScriptFileName);
await using (var stream = File.CreateText(upgradeScriptFilePath))
{
stream.NewLine = "\n";
await stream.WriteLineAsync("#!/bin/bash");
await stream.WriteLineAsync();
await stream.WriteLineAsync("testcmd() { command -v \"$1\" &> /dev/null; }");
await stream.WriteLineAsync();
await stream.WriteLineAsync("cd \"$(dirname \"$0\")\"");
await stream.WriteLineAsync($"echo '{About.Software} v{About.VersionStr} updater script'");
await stream.WriteLineAsync();
//await stream.WriteLineAsync($"cd '{App.ApplicationPath}'");
await stream.WriteLineAsync($"killall {About.Software}");
await stream.WriteLineAsync("sleep 1");
await stream.WriteLineAsync();
if (OperatingSystem.IsMacOS())
{
await stream.WriteLineAsync($"find '{extractDirectoryPath}' -print0 | xargs -0 xattr -d com.apple.quarantine &> /dev/null");
}
if (macOS.IsRunningAppGetPath(out var macOSAppPath) && Directory.Exists(Path.Combine(extractDirectoryPath, "UVtools.app")))
{
await stream.WriteLineAsync("if testcmd rsync; then");
await stream.WriteLineAsync($" rsync -arctxv --delete --remove-source-files --stats '{Path.Combine(extractDirectoryPath, "UVtools.app")}/' '{macOSAppPath}'");
await stream.WriteLineAsync("else");
await stream.WriteLineAsync($" cp -fR '{extractDirectoryPath}/'* '{macOSAppPath}'");
await stream.WriteLineAsync("fi");
//await stream.WriteLineAsync($"open '{macOSAppPath}'");
}
else // Linux generic and macOS generic
{
await stream.WriteLineAsync("if testcmd rsync; then");
await stream.WriteLineAsync($" rsync -arctxv --remove-source-files --stats '{extractDirectoryPath}/' '{App.ApplicationPath}'");
await stream.WriteLineAsync("else");
await stream.WriteLineAsync($" cp -fR '{extractDirectoryPath}/'* '{App.ApplicationPath}'");
await stream.WriteLineAsync("fi");
}
await stream.WriteLineAsync();
await stream.WriteLineAsync($"nohup bash '{Path.Combine(App.ApplicationPath, "UVtools.sh")}' &> /dev/null &");
await stream.WriteLineAsync("disown");
await stream.WriteLineAsync();
await stream.WriteLineAsync($"rm -fr '{tmpDirectory}'");
//await stream.WriteLineAsync("sleep 0.5");
//await stream.WriteLineAsync($"rm -f {upgradeScriptFileName}");
//await stream.WriteLine("exit");
}
SystemAware.StartProcess("bash", $"\"{upgradeScriptFilePath}\"");
//App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
}
Environment.Exit(0);
return true;
}
catch (OperationCanceledException)
{