Przeglądaj źródła

macos autoupdate stuff

Krzysztof Krysiński 3 miesięcy temu
rodzic
commit
714d5708c3

+ 1 - 1
src/ColorPicker

@@ -1 +1 @@
-Subproject commit 78237e9c5d70ebd878b34f4dd63f54b25bafea23
+Subproject commit 56faff5afcc6fc96b44b5caa6f15d0b3a902cd0a

+ 3 - 2
src/PixiEditor.AnimationRenderer.FFmpeg/FFMpegRenderer.cs

@@ -173,11 +173,12 @@ public class FFMpegRenderer : IAnimationRenderer
                 return;
             }
 
-            IOperatingSystem.Current.ProcessUtility.Execute("chmod", $"+x {filePath}");
+            IOperatingSystem.Current.ProcessUtility.Execute("chmod", $"+x {filePath}").WaitForExit(500);
         }
         catch (Exception e)
         {
-            IOperatingSystem.Current.ProcessUtility.Execute("chmod", $"+x {filePath}");
+            IOperatingSystem.Current.ProcessUtility.Execute("chmod", $"+x {filePath}")
+                .WaitForExit(500);
         }
     }
 

+ 8 - 4
src/PixiEditor.MacOs/MacOsProcessUtility.cs

@@ -7,12 +7,16 @@ internal class MacOsProcessUtility : IProcessUtility
 {
     public Process RunAsAdmin(string path, string args)
     {
+        string script = $"""
+
+                                     do shell script "'{path}' {args}" with administrator privileges
+                         """;
         ProcessStartInfo startInfo = new ProcessStartInfo
         {
-            FileName = path,
-            Verb = "runas",
-            Arguments = args,
-            UseShellExecute = true
+            FileName = "osascript",
+            ArgumentList = { "-e", script },
+            UseShellExecute = true,
+            CreateNoWindow = true,
         };
 
         Process p = new Process();

+ 48 - 19
src/PixiEditor.UpdateInstaller.Exe/Program.cs

@@ -5,6 +5,7 @@ using PixiEditor.UpdateInstaller.ViewModels;
 UpdateController controller = new UpdateController();
 StringBuilder log = new StringBuilder();
 bool startAfterUpdate = false;
+string logDirectory = Path.Combine(Path.GetTempPath(), "PixiEditor");
 
 foreach (string arg in args)
 {
@@ -20,37 +21,27 @@ try
 {
     log.AppendLine($"{DateTime.Now}: Starting update installation...");
     controller.InstallUpdate(log);
+    log.AppendLine($"{DateTime.Now}: Update installation completed successfully.");
 }
 catch (Exception ex)
 {
     log.AppendLine($"{DateTime.Now}: Error during update installation: {ex.Message}");
-    File.AppendAllText("ErrorLog.txt",
-        $"Error PixiEditor.UpdateInstaller: {DateTime.Now}\n{ex.Message}\n{ex.StackTrace}\n-----\n");
+    string errorLogPath = Path.Combine(logDirectory, "ErrorLog.txt");
+    File.AppendAllText(errorLogPath, $"Error PixiEditor.UpdateInstaller: {DateTime.Now}\n{ex.Message}\n{ex.StackTrace}\n-----\n");
 }
 finally
 {
-    try
-    {
-        File.WriteAllText("UpdateLog.txt", log.ToString());
-    }
-    catch
-    {
-        // probably permissions or disk full, the best we can do is to ignore this
-    }
-
     if (startAfterUpdate)
     {
-        string binaryName = OperatingSystem.IsWindows() ? "PixiEditor.exe" : "PixiEditor";
-        var files = Directory.GetFiles(controller.UpdateDirectory, binaryName);
-        if (files.Length > 0)
+        log.AppendLine($"{DateTime.Now}: Starting PixiEditor after update.");
+        if (OperatingSystem.IsMacOS())
         {
-            string pixiEditorExecutablePath = files[0];
-            Process.Start(pixiEditorExecutablePath);
+            StartPixiEditorOnMacOS(controller);
         }
         else
         {
-            binaryName = OperatingSystem.IsWindows() ? "PixiEditor.Desktop.exe" : "PixiEditor.Desktop";
-            files = Directory.GetFiles(controller.UpdateDirectory, binaryName);
+            string binaryName = OperatingSystem.IsWindows() ? "PixiEditor.exe" : "PixiEditor";
+            var files = Directory.GetFiles(controller.UpdateDirectory, binaryName);
             if (files.Length > 0)
             {
                 string pixiEditorExecutablePath = files[0];
@@ -58,8 +49,46 @@ finally
             }
             else
             {
-                Console.WriteLine("PixiEditor executable not found.");
+                binaryName = OperatingSystem.IsWindows() ? "PixiEditor.Desktop.exe" : "PixiEditor.Desktop";
+                files = Directory.GetFiles(controller.UpdateDirectory, binaryName);
+                if (files.Length > 0)
+                {
+                    string pixiEditorExecutablePath = files[0];
+                    Process.Start(pixiEditorExecutablePath);
+                }
+                else
+                {
+                    Console.WriteLine("PixiEditor executable not found.");
+                }
             }
         }
     }
+    
+    try
+    {
+        string updateLogPath = Path.Combine(logDirectory, "UpdateLog.txt");
+        File.WriteAllText(updateLogPath, log.ToString());
+    }
+    catch
+    {
+        // probably permissions or disk full, the best we can do is to ignore this
+    }
+}
+
+void StartPixiEditorOnMacOS(UpdateController controller)
+{
+    string pixiEditorExecutablePath = Path.Combine(controller.UpdateDirectory, "PixiEditor.app");
+    if (Directory.Exists(pixiEditorExecutablePath))
+    {
+        log.AppendLine($"{DateTime.Now}: Starting PixiEditor with open -a PixiEditor");
+        Process.Start(new ProcessStartInfo
+        {
+            FileName = "open",
+            Arguments = $"-a PixiEditor",
+        });
+    }
+    else
+    {
+        log.AppendLine($"{DateTime.Now}: PixiEditor.app not found at {pixiEditorExecutablePath}");
+    }
 }

+ 4 - 0
src/PixiEditor.UpdateInstaller/PixiEditor.UpdateInstaller/ViewModels/UpdateController.cs

@@ -13,6 +13,10 @@ public class UpdateController
         Current = this;
 
         string updateDirectory = Path.GetDirectoryName(Extensions.GetExecutablePath());
+        if (OperatingSystem.IsMacOS())
+        {
+            updateDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
+        }
 
 #if DEBUG
         updateDirectory = Path.GetDirectoryName(Environment.GetCommandLineArgs().FirstOrDefault());

+ 5 - 0
src/PixiEditor.UpdateModule/UpdateChecker.cs

@@ -78,6 +78,11 @@ public class UpdateChecker
         LatestReleaseInfo = await GetLatestReleaseInfoAsync(Channel.ApiUrl);
         return CheckUpdateAvailable(LatestReleaseInfo);
     }
+    
+    public void SetLatestReleaseInfo(ReleaseInfo releaseInfo)
+    {
+        LatestReleaseInfo = releaseInfo;
+    }
 
     public bool CheckUpdateAvailable(ReleaseInfo latestRelease)
     {

+ 3 - 2
src/PixiEditor.UpdateModule/UpdateDownloader.cs

@@ -70,8 +70,9 @@ public static class UpdateDownloader
                                                       && x.Name.Contains(archOld));
         }
 
-        string arch = OperatingSystem.IsWindows() ? "x64" : "amd64";
-        string os = OperatingSystem.IsWindows() ? "win" : OperatingSystem.IsLinux() ? "linux" : "mac";
+        string arch = OperatingSystem.IsWindows() ? "x64" : 
+                      OperatingSystem.IsLinux() ? "amd64" : "universal";
+        string os = OperatingSystem.IsWindows() ? "win" : OperatingSystem.IsLinux() ? "linux" : "macos";
         return release.Assets.FirstOrDefault(x => x.ContentType.Contains(assetType)
                                                   && x.Name.Contains(arch) && x.Name.Contains(os));
     }

+ 62 - 14
src/PixiEditor.UpdateModule/UpdateInstaller.cs

@@ -3,6 +3,7 @@ using System.Diagnostics;
 using System.Formats.Tar;
 using System.IO;
 using System.IO.Compression;
+using System.Linq;
 using System.Text;
 
 namespace PixiEditor.UpdateModule;
@@ -17,7 +18,8 @@ public class UpdateInstaller
         TargetDirectory = targetDirectory;
     }
 
-    public static string UpdateFilesPath { get; set; } = Path.Join(UpdateDownloader.DownloadLocation, TargetDirectoryName);
+    public static string UpdateFilesPath { get; set; } =
+        Path.Join(UpdateDownloader.DownloadLocation, TargetDirectoryName);
 
     public string ArchiveFileName { get; set; }
 
@@ -26,34 +28,78 @@ public class UpdateInstaller
     public void Install(StringBuilder log)
     {
         var processes = Process.GetProcessesByName("PixiEditor.Desktop");
+        processes = processes.Concat(Process.GetProcessesByName("PixiEditor")).ToArray();
         log.AppendLine($"Found {processes.Length} PixiEditor processes running.");
         if (processes.Length > 0)
         {
             log.AppendLine("Killing PixiEditor processes...");
-            processes[0].WaitForExit();
+            foreach (var process in processes)
+            {
+                try
+                {
+                    log.AppendLine($"Killing process {process.ProcessName} with ID {process.Id}");
+                    process.Kill();
+                    process.WaitForExit();
+                }
+                catch (Exception ex)
+                {
+                    log.AppendLine($"Failed to kill process {process.ProcessName} with ID {process.Id}: {ex.Message}");
+                }
+            }
             log.AppendLine("Processes killed.");
         }
-        
+
         log.AppendLine("Extracting files");
-        
-        if(Directory.Exists(UpdateFilesPath))
+
+        if (Directory.Exists(UpdateFilesPath))
         {
             Directory.Delete(UpdateFilesPath, true);
         }
-        
+
         Directory.CreateDirectory(UpdateFilesPath);
-        
+
         bool isZip = ArchiveFileName.EndsWith(".zip");
         if (isZip)
         {
+            log.AppendLine($"Extracting {ArchiveFileName} to {UpdateFilesPath}");
             ZipFile.ExtractToDirectory(ArchiveFileName, UpdateFilesPath, true);
         }
         else
         {
+            log.AppendLine($"Extracting {ArchiveFileName} to {UpdateFilesPath} using GZipStream");
             using FileStream fs = new(ArchiveFileName, FileMode.Open, FileAccess.Read);
             using GZipStream gz = new(fs, CompressionMode.Decompress, leaveOpen: true);
 
-            TarFile.ExtractToDirectory(gz, UpdateFilesPath, overwriteFiles: false);        
+            TarFile.ExtractToDirectory(gz, UpdateFilesPath, overwriteFiles: true);
+        }
+
+        if (OperatingSystem.IsMacOS())
+        {
+            string appFile = Directory.GetDirectories(UpdateFilesPath, "PixiEditor.app", SearchOption.TopDirectoryOnly)
+                .FirstOrDefault();
+            if (string.IsNullOrEmpty(appFile))
+            {
+                log.AppendLine("PixiEditor.app not found in the update files. Installation failed.");
+                string[] allFiles = Directory.GetFiles(UpdateFilesPath, "*.*", SearchOption.TopDirectoryOnly);
+                foreach (string file in allFiles)
+                {
+                    log.AppendLine($"Found file: {file}");
+                }
+                throw new FileNotFoundException("PixiEditor.app not found in the update files.");
+            }
+
+            
+            log.AppendLine($"Moving {appFile} to {TargetDirectory}");
+            string targetAppDirectory = Path.Combine(TargetDirectory, "PixiEditor.app");
+            if (Directory.Exists(targetAppDirectory))
+            {
+                log.AppendLine($"Removing existing PixiEditor.app at {targetAppDirectory}");
+                Directory.Delete(targetAppDirectory, true);
+            }
+            Directory.Move(appFile, targetAppDirectory);
+            
+            DeleteArchive();
+            return;
         }
 
         string[] extractedFiles = Directory.GetFiles(UpdateFilesPath, "*", SearchOption.AllDirectories);
@@ -66,16 +112,18 @@ public class UpdateInstaller
         {
             dirWithFiles = Directory.GetDirectories(UpdateFilesPath)[0];
         }
-        
-        string updaterFile = Path.Combine(dirWithFiles, "PixiEditor.UpdateInstaller" + (OperatingSystem.IsWindows() ? ".exe" : ""));
+
+        string updaterFile = Path.Combine(dirWithFiles,
+            "PixiEditor.UpdateInstaller" + (OperatingSystem.IsWindows() ? ".exe" : ""));
 
         if (File.Exists(updaterFile))
         {
-            string newName = Path.Combine(dirWithFiles, "PixiEditor.UpdateInstaller-update" + (OperatingSystem.IsWindows() ? ".exe" : ""));
+            string newName = Path.Combine(dirWithFiles,
+                "PixiEditor.UpdateInstaller-update" + (OperatingSystem.IsWindows() ? ".exe" : ""));
             File.Move(updaterFile, newName);
             log.AppendLine($"Renamed {updaterFile} to {newName}");
         }
-        
+
         log.AppendLine($"Copying files from {dirWithFiles} to {TargetDirectory}");
 
         try
@@ -123,12 +171,12 @@ public class UpdateInstaller
     {
         string[] subDirs = Directory.GetDirectories(originDirectory);
         log.AppendLine($"Found {subDirs.Length} subdirectories to copy.");
-        if(subDirs.Length == 0) return;
+        if (subDirs.Length == 0) return;
 
         foreach (string subDir in subDirs)
         {
             string targetDirPath = Path.Join(targetDirectory, Path.GetFileName(subDir));
-            
+
             log.AppendLine($"Copying {subDir} to {targetDirPath}");
 
             CopySubDirectories(subDir, targetDirPath, log);

+ 46 - 35
src/PixiEditor/ViewModels/SubViewModels/UpdateViewModel.cs

@@ -247,7 +247,8 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
 
     private void Install(bool startAfterUpdate)
     {
-        string dir = AppDomain.CurrentDomain.BaseDirectory;
+        string dir = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName) ??
+                     AppDomain.CurrentDomain.BaseDirectory;
 
         UpdateDownloader.CreateTempDirectory();
         if (UpdateChecker.LatestReleaseInfo == null ||
@@ -267,6 +268,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
 
         string updaterPath = Path.Join(dir, $"PixiEditor.UpdateInstaller{BinaryExtension}");
 
+
         if (!updateFileExists && !updateExeExists)
         {
             EnsureUpdateFilesDeleted();
@@ -274,6 +276,12 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             Dispatcher.UIThread.InvokeAsync(async () => await CheckForUpdate());
             return;
         }
+        
+        if (IOperatingSystem.Current.IsMacOs && Path.Exists(updaterPath))
+        {
+            File.Copy(updaterPath, Path.Join(UpdateDownloader.DownloadLocation, $"PixiEditor.UpdateInstaller"), true);
+            updaterPath = Path.Join(UpdateDownloader.DownloadLocation, $"PixiEditor.UpdateInstaller");
+        }
 
         if (updateFileExists && File.Exists(updaterPath))
         {
@@ -287,36 +295,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
 
     private static void InstallHeadless(string updaterPath, bool startAfterUpdate)
     {
-        try
-        {
-            if (IOperatingSystem.Current.IsLinux)
-            {
-                bool hasWritePermissions = !InstallDirReadOnly();
-                if (hasWritePermissions)
-                {
-                    IOperatingSystem.Current.ProcessUtility.ShellExecute(updaterPath, startAfterUpdate ? "--startOnSuccess" : null);
-                }
-                else
-                {
-                    NoticeDialog.Show(
-                        "COULD_NOT_UPDATE_WITHOUT_ADMIN",
-                        "INSUFFICIENT_PERMISSIONS");
-                    return;
-                }
-            }
-            else
-            {
-                ProcessHelper.RunAsAdmin(updaterPath, startAfterUpdate ? "--startOnSuccess" : null);
-            }
-
-            Shutdown();
-        }
-        catch (Win32Exception)
-        {
-            NoticeDialog.Show(
-                "COULD_NOT_UPDATE_WITHOUT_ADMIN",
-                "INSUFFICIENT_PERMISSIONS");
-        }
+        TryRestartToUpdate(updaterPath, startAfterUpdate ? "--startOnSuccess" : null);
     }
 
     private void OpenExeInstaller(string updateExeFile)
@@ -342,6 +321,11 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
     }
 
     private static void RestartToUpdate(string updateExeFile)
+    {
+        TryRestartToUpdate(updateExeFile, null);
+    }
+
+    private static void TryRestartToUpdate(string updateExeFile, string? args)
     {
         try
         {
@@ -351,15 +335,42 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
                 bool hasWritePermissions = !InstallDirReadOnly();
                 if (hasWritePermissions)
                 {
-                    IOperatingSystem.Current.ProcessUtility.ShellExecute(updateExeFile, null);
+                    IOperatingSystem.Current.ProcessUtility.ShellExecute(updateExeFile, args);
+                    Shutdown();
+                }
+                else
+                {
+                    NoticeDialog.Show("COULD_NOT_UPDATE_WITHOUT_ADMIN", "INSUFFICIENT_PERMISSIONS");
+                    return;
                 }
             }
             else
             {
-                IOperatingSystem.Current.ProcessUtility.RunAsAdmin(updateExeFile, null);
+                var proc = IOperatingSystem.Current.ProcessUtility.RunAsAdmin(updateExeFile, args);
+                if (IOperatingSystem.Current.IsMacOs)
+                {
+                    proc.WaitForExitAsync().ContinueWith(t =>
+                    {
+                        Dispatcher.UIThread.Invoke(() =>
+                        {
+
+                            if (t.IsCompletedSuccessfully && proc.ExitCode == 0)
+                            {
+                                Shutdown();
+                            }
+                            else
+                            {
+                                NoticeDialog.Show("COULD_NOT_UPDATE_WITHOUT_ADMIN", "INSUFFICIENT_PERMISSIONS");
+                            }
+                        });
+                    });
+                }
+                else
+                {
+                    Shutdown();
+                }
             }
 
-            Shutdown();
         }
         catch (Win32Exception)
         {
@@ -431,7 +442,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
 
     private bool OsSupported()
     {
-        return IOperatingSystem.Current.IsWindows || IOperatingSystem.Current.IsLinux;
+        return IOperatingSystem.Current.IsWindows || IOperatingSystem.Current.IsLinux || IOperatingSystem.Current.IsMacOs;
     }
 
     private void EnsureUpdateFilesDeleted()