diff --git a/src/UniGetUI.Core.Data/CoreData.cs b/src/UniGetUI.Core.Data/CoreData.cs
index 5c17b6a055..1eac17c0db 100644
--- a/src/UniGetUI.Core.Data/CoreData.cs
+++ b/src/UniGetUI.Core.Data/CoreData.cs
@@ -1,7 +1,4 @@
using System.Diagnostics;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using System.Text.Json.Serialization.Metadata;
using UniGetUI.Core.Logging;
namespace UniGetUI.Core.Data
diff --git a/src/UniGetUI.Core.LanguageEngine/LanguageData.cs b/src/UniGetUI.Core.LanguageEngine/LanguageData.cs
index 8c374f7e64..07e6d10cc6 100644
--- a/src/UniGetUI.Core.LanguageEngine/LanguageData.cs
+++ b/src/UniGetUI.Core.LanguageEngine/LanguageData.cs
@@ -1,11 +1,9 @@
using System.Collections.ObjectModel;
-using System.Runtime.InteropServices;
using System.Text.Json.Nodes;
using UniGetUI.Core.Classes;
using UniGetUI.Core.Data;
using UniGetUI.Core.Logging;
using UniGetUI.PackageEngine.Enums;
-using Architecture = UniGetUI.PackageEngine.Enums.Architecture;
namespace UniGetUI.Core.Language
{
diff --git a/src/UniGetUI.Core.SecureSettings/SecureSettings.cs b/src/UniGetUI.Core.SecureSettings/SecureSettings.cs
index 11b2d0044e..7ffe30fbc7 100644
--- a/src/UniGetUI.Core.SecureSettings/SecureSettings.cs
+++ b/src/UniGetUI.Core.SecureSettings/SecureSettings.cs
@@ -1,8 +1,6 @@
using System.Diagnostics;
using UniGetUI.Core.Data;
using UniGetUI.Core.Tools;
-using YamlDotNet.Core.Tokens;
-using YamlDotNet.Serialization;
namespace UniGetUI.Core.SettingsEngine.SecureSettings;
diff --git a/src/UniGetUI.Core.Settings/SettingsEngine_Names.cs b/src/UniGetUI.Core.Settings/SettingsEngine_Names.cs
index e9a5054fea..33a55de508 100644
--- a/src/UniGetUI.Core.Settings/SettingsEngine_Names.cs
+++ b/src/UniGetUI.Core.Settings/SettingsEngine_Names.cs
@@ -74,6 +74,7 @@ public enum K
DisableErrorNotifications,
DisableSuccessNotifications,
DisableProgressNotifications,
+ KillProcessesThatRefuseToDie,
Test1,
Test2,
@@ -159,6 +160,7 @@ public static string ResolveKey(K key)
K.DisableErrorNotifications => "DisableErrorNotifications",
K.DisableSuccessNotifications => "DisableSuccessNotifications",
K.DisableProgressNotifications => "DisableProgressNotifications",
+ K.KillProcessesThatRefuseToDie => "KillProcessesThatRefuseToDie",
K.Test1 => "TestSetting1",
K.Test2 => "TestSetting2",
diff --git a/src/UniGetUI.Core.Tools/SerializationHelpers.cs b/src/UniGetUI.Core.Tools/SerializationHelpers.cs
index be129d130b..4172b94dc2 100644
--- a/src/UniGetUI.Core.Tools/SerializationHelpers.cs
+++ b/src/UniGetUI.Core.Tools/SerializationHelpers.cs
@@ -1,7 +1,6 @@
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Nodes;
-using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Xml;
diff --git a/src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs b/src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs
index e9f14bf2af..ad224ea79e 100644
--- a/src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs
+++ b/src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs
@@ -1,5 +1,3 @@
-using System.Runtime.InteropServices;
-
namespace UniGetUI.PackageEngine.ManagerClasses.Manager
{
public enum ProxySupport
diff --git a/src/UniGetUI.PackageEngine.Enums/OverridenInstallationOptions.cs b/src/UniGetUI.PackageEngine.Enums/OverridenInstallationOptions.cs
index 052363b0d0..75bdc7df31 100644
--- a/src/UniGetUI.PackageEngine.Enums/OverridenInstallationOptions.cs
+++ b/src/UniGetUI.PackageEngine.Enums/OverridenInstallationOptions.cs
@@ -1,5 +1,3 @@
-using UniGetUI.PackageEngine.Enums;
-
namespace UniGetUI.PackageEngine.Structs;
public struct OverridenInstallationOptions
{
diff --git a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs
index d76cbe26e8..0a3c486d9b 100644
--- a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs
+++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs
@@ -1,5 +1,4 @@
using System.Diagnostics;
-using System.Runtime.InteropServices;
using UniGetUI.Core.Data;
using UniGetUI.Core.Logging;
using UniGetUI.Core.SettingsEngine;
diff --git a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs
index b9e5d70f3b..86b962f272 100644
--- a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs
+++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs
@@ -1,4 +1,3 @@
-using System.Runtime.InteropServices;
using UniGetUI.PackageEngine.Classes.Manager.BaseProviders;
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Interfaces;
diff --git a/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs b/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs
index e1476affdf..82225200c6 100644
--- a/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs
+++ b/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs
@@ -1,5 +1,4 @@
using System.Diagnostics;
-using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using UniGetUI.Core.Tools;
using UniGetUI.Interface.Enums;
diff --git a/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs
index 210ce71977..944e03f0b5 100644
--- a/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs
+++ b/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs
@@ -1,4 +1,3 @@
-using System.Runtime.InteropServices;
using UniGetUI.PackageEngine.Classes.Manager.BaseProviders;
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Interfaces;
diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs
index 58cb4214a9..0443aaaa6d 100644
--- a/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs
+++ b/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs
@@ -1,4 +1,3 @@
-using System.Runtime.InteropServices;
using UniGetUI.PackageEngine.Classes.Manager.BaseProviders;
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Interfaces;
diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs b/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs
index 049ae08f65..65c4176cb4 100644
--- a/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs
+++ b/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs
@@ -1,5 +1,4 @@
using System.Diagnostics;
-using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using UniGetUI.Core.Classes;
using UniGetUI.Core.Logging;
diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativePackageHandler.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativePackageHandler.cs
index 5dc9607547..c3f8149d3a 100644
--- a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativePackageHandler.cs
+++ b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativePackageHandler.cs
@@ -1,10 +1,8 @@
using System.Collections.Concurrent;
-using System.Linq.Expressions;
using Microsoft.Management.Deployment;
using UniGetUI.Core.Logging;
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Interfaces;
-using UniGetUI.PackageEngine.Serializable;
using InstallOptions = UniGetUI.PackageEngine.Serializable.InstallOptions;
namespace UniGetUI.PackageEngine.Managers.WingetManager;
diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/WinGetIconsHelper.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/WinGetIconsHelper.cs
index 7fee66356a..8523ac33c0 100644
--- a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/WinGetIconsHelper.cs
+++ b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/WinGetIconsHelper.cs
@@ -1,7 +1,5 @@
using System.Globalization;
using System.Net;
-using System.Reflection;
-using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Management.Deployment;
using Microsoft.Win32;
diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgDetailsHelper.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgDetailsHelper.cs
index 2ebf3b4d49..71ee33ab9d 100644
--- a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgDetailsHelper.cs
+++ b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgDetailsHelper.cs
@@ -1,8 +1,6 @@
-using System.ComponentModel.Design;
using System.Text.RegularExpressions;
using UniGetUI.Core.IconEngine;
using UniGetUI.Core.Logging;
-using UniGetUI.Core.Tools;
using UniGetUI.PackageEngine.Classes.Manager.BaseProviders;
using UniGetUI.PackageEngine.Interfaces;
using UniGetUI.PackageEngine.Managers.WinGet.ClientHelpers;
diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs
index e1e8b0b47d..fbe0c818da 100644
--- a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs
+++ b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs
@@ -1,4 +1,3 @@
-using System.Runtime.InteropServices;
using Microsoft.Management.Deployment;
using UniGetUI.Core.Logging;
using UniGetUI.Core.SettingsEngine;
@@ -7,7 +6,6 @@
using UniGetUI.PackageEngine.Classes.Packages.Classes;
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Interfaces;
-using UniGetUI.PackageEngine.Serializable;
using Architecture = UniGetUI.PackageEngine.Enums.Architecture;
using InstallOptions = UniGetUI.PackageEngine.Serializable.InstallOptions;
diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs
index 353f50b248..b9964b730c 100644
--- a/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs
+++ b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs
@@ -1,6 +1,4 @@
-using System.ComponentModel;
using System.Diagnostics;
-using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text;
diff --git a/src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs b/src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs
index f19bc881ce..45366c932e 100644
--- a/src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs
+++ b/src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs
@@ -5,64 +5,9 @@
namespace UniGetUI.PackageOperations;
-public abstract class AbstractOperation : IDisposable
+public abstract partial class AbstractOperation : IDisposable
{
- public static class RetryMode
- {
- public const string NoRetry = "";
- public const string Retry = "Retry";
- public const string Retry_AsAdmin = "RetryAsAdmin";
- public const string Retry_Interactive = "RetryInteractive";
- public const string Retry_SkipIntegrity = "RetryNoHashCheck";
- }
-
- public class OperationMetadata
- {
- ///
- /// Installation of X
- ///
- public string Title = "";
-
- ///
- /// X is being installed/upated/removed
- ///
- public string Status = "";
-
- ///
- /// X was installed
- ///
- public string SuccessTitle = "";
-
- ///
- /// X has been installed successfully
- ///
- public string SuccessMessage = "";
-
- ///
- /// X could not be installed.
- ///
- public string FailureTitle = "";
-
- ///
- /// X Could not be installed
- ///
- public string FailureMessage = "";
-
- ///
- /// Starting operation X with options Y
- ///
- public string OperationInformation = "";
-
- public readonly string Identifier;
-
- public OperationMetadata()
- {
- Identifier = new Random().NextInt64(1000000, 9999999).ToString();
- }
- }
-
public readonly OperationMetadata Metadata = new();
- public static readonly List OperationQueue = [];
public event EventHandler? StatusChanged;
public event EventHandler? CancelRequested;
@@ -72,38 +17,12 @@ public OperationMetadata()
public event EventHandler? Enqueued;
public event EventHandler? OperationSucceeded;
public event EventHandler? OperationFailed;
-
- public static int MAX_OPERATIONS;
-
public event EventHandler? BadgesChanged;
- public class BadgeCollection
- {
- public readonly bool AsAdministrator;
- public readonly bool Interactive;
- public readonly bool SkipHashCheck;
- public readonly string? Scope;
-
- public BadgeCollection(bool admin, bool interactive, bool skiphash, string? scope)
- {
- AsAdministrator = admin;
- Interactive = interactive;
- SkipHashCheck = skiphash;
- Scope = scope;
- }
- }
- public void ApplyCapabilities(bool admin, bool interactive, bool skiphash, string? scope)
- {
- BadgesChanged?.Invoke(this, new BadgeCollection(admin, interactive, skiphash, scope));
- }
-
- public enum LineType
- {
- VerboseDetails,
- ProgressIndicator,
- Information,
- Error
- }
+ public bool Started { get; private set; }
+ protected bool QUEUE_ENABLED;
+ protected bool FORCE_HOLD_QUEUE;
+ private bool IsInnerOperation;
private readonly List<(string, LineType)> LogList = [];
private OperationStatus _status = OperationStatus.InQueue;
@@ -113,20 +32,22 @@ public OperationStatus Status
set { _status = value; StatusChanged?.Invoke(this, value); }
}
- public bool Started { get; private set; }
- protected bool QUEUE_ENABLED;
- protected bool FORCE_HOLD_QUEUE;
+ public void ApplyCapabilities(bool admin, bool interactive, bool skiphash, string? scope)
+ {
+ BadgesChanged?.Invoke(this, new BadgeCollection(admin, interactive, skiphash, scope));
+ }
- private readonly AbstractOperation? requirement;
+ private readonly IReadOnlyList PreOperations = [];
+ private readonly IReadOnlyList PostOperations = [];
- public AbstractOperation(bool queue_enabled, AbstractOperation? req)
+ public AbstractOperation(
+ bool queue_enabled,
+ IReadOnlyList? preOps = null,
+ IReadOnlyList? postOps = null)
{
QUEUE_ENABLED = queue_enabled;
- if (req is not null)
- {
- requirement = req;
- QUEUE_ENABLED = false;
- }
+ if (preOps is not null) PreOperations = preOps;
+ if (postOps is not null) PostOperations = postOps;
Status = OperationStatus.InQueue;
Line("Please wait...", LineType.ProgressIndicator);
@@ -202,20 +123,9 @@ public async Task MainThread()
Enqueued?.Invoke(this, EventArgs.Empty);
- if (requirement != null)
- { // OPERATION REQUIREMENT HANDLER
- Logger.Info($"Operation {Metadata.Title} is waiting for requirement operation {requirement.Metadata.Title}");
- Line(CoreTools.Translate("Waiting for {0} to complete...", requirement.Metadata.Title), LineType.ProgressIndicator);
- {
- while (requirement.Status is OperationStatus.Running or OperationStatus.InQueue)
- {
- await Task.Delay(100);
- if (SKIP_QUEUE) break;
- }
- }
- }
- else if (QUEUE_ENABLED)
- { // QUEUE HANDLER
+ if (QUEUE_ENABLED && !IsInnerOperation)
+ {
+ // QUEUE HANDLER
SKIP_QUEUE = false;
OperationQueue.Add(this);
int lastPos = -2;
@@ -238,61 +148,8 @@ public async Task MainThread()
}
// END QUEUE HANDLER
- // BEGIN ACTUAL OPERATION
- OperationVeredict result;
- Line(CoreTools.Translate("Starting operation..."), LineType.ProgressIndicator);
- if (Status is OperationStatus.InQueue) Status = OperationStatus.Running;
-
- do
- {
- OperationStarting?.Invoke(this, EventArgs.Empty);
-
- try
- {
- // Check if the operation was canceled
- if (Status is OperationStatus.Canceled)
- {
- result = OperationVeredict.Canceled;
- break;
- }
-
- if (requirement is not null)
- {
- if (requirement.Status is OperationStatus.Failed)
- {
- Line(CoreTools.Translate("{0} has failed, that was a requirement for {1} to be run", requirement.Metadata.Title, Metadata.Title), LineType.Error);
- result = OperationVeredict.Failure;
- break;
- }
-
- if (requirement.Status is OperationStatus.Canceled)
- {
- Line(CoreTools.Translate("The user has canceled {0}, that was a requirement for {1} to be run", requirement.Metadata.Title, Metadata.Title), LineType.Error);
- result = OperationVeredict.Canceled;
- break;
- }
- }
-
- Task op = PerformOperation();
- while (Status != OperationStatus.Canceled && !op.IsCompleted) await Task.Delay(100);
-
- if (Status is OperationStatus.Canceled) result = OperationVeredict.Canceled;
- else result = op.GetAwaiter().GetResult();
- }
- catch (Exception e)
- {
- result = OperationVeredict.Failure;
- Logger.Error(e);
- foreach (string l in e.ToString().Split("\n"))
- {
- Line(l, LineType.Error);
- }
- }
- } while (result == OperationVeredict.AutoRetry);
-
-
+ var result = await _runOperation();
while (OperationQueue.Remove(this));
- // END OPERATION
if (result == OperationVeredict.Success)
{
@@ -352,6 +209,91 @@ public async Task MainThread()
}
}
+ private async Task _runOperation()
+ {
+ OperationVeredict result;
+
+ // Process preoperations
+ int i = 0, count = PreOperations.Count;
+ if(count > 0) Line("", LineType.VerboseDetails);
+ foreach (var preReq in PreOperations)
+ {
+ i++;
+ Line(CoreTools.Translate($"Running PreOperation ({i}/{count})..."), LineType.Information);
+ preReq.Operation.LogLineAdded += (_, line) => Line(line.Item1, line.Item2);
+ await preReq.Operation.MainThread();
+ if (preReq.Operation.Status is not OperationStatus.Succeeded && preReq.MustSucceed)
+ {
+ Line(
+ CoreTools.Translate($"PreOperation {i} out of {count} failed, and was tagged as necessary. Aborting..."),
+ LineType.Error);
+ return OperationVeredict.Failure;
+ }
+ Line(CoreTools.Translate($"PreOperation {i} out of {count} finished with result {preReq.Operation.Status}"), LineType.Information);
+ Line("--------------------------------", LineType.Information);
+ Line("", LineType.VerboseDetails);
+ }
+
+ // BEGIN ACTUAL OPERATION
+ Line(CoreTools.Translate("Starting operation..."), LineType.Information);
+ if (Status is OperationStatus.InQueue) Status = OperationStatus.Running;
+
+ do
+ {
+ OperationStarting?.Invoke(this, EventArgs.Empty);
+
+ try
+ {
+ // Check if the operation was canceled
+ if (Status is OperationStatus.Canceled)
+ {
+ result = OperationVeredict.Canceled;
+ break;
+ }
+
+ Task op = PerformOperation();
+ while (Status != OperationStatus.Canceled && !op.IsCompleted) await Task.Delay(100);
+
+ if (Status is OperationStatus.Canceled) result = OperationVeredict.Canceled;
+ else result = op.GetAwaiter().GetResult();
+ }
+ catch (Exception e)
+ {
+ result = OperationVeredict.Failure;
+ Logger.Error(e);
+ foreach (string l in e.ToString().Split("\n"))
+ {
+ Line(l, LineType.Error);
+ }
+ }
+ } while (result is OperationVeredict.AutoRetry);
+
+ if (result is not OperationVeredict.Success)
+ return result;
+
+ // Process postoperations
+ i = 0; count = PostOperations.Count;
+ foreach (var postReq in PostOperations)
+ {
+ i++;
+ Line("--------------------------------", LineType.Information);
+ Line("", LineType.VerboseDetails);
+ Line(CoreTools.Translate($"Running PostOperation ({i}/{count})..."), LineType.Information);
+ postReq.Operation.LogLineAdded += (_, line) => Line(line.Item1, line.Item2);
+ await postReq.Operation.MainThread();
+ if (postReq.Operation.Status is not OperationStatus.Succeeded && postReq.MustSucceed)
+ {
+ Line(
+ CoreTools.Translate($"PostOperation {i} out of {count} failed, and was tagged as necessary. Aborting..."),
+ LineType.Error);
+ return OperationVeredict.Failure;
+ }
+ Line(CoreTools.Translate($"PostOperation {i} out of {count} finished with result {postReq.Operation.Status}"), LineType.Information);
+ }
+
+ return result;
+ }
+
private bool SKIP_QUEUE;
public void SkipQueue()
diff --git a/src/UniGetUI.PackageEngine.Operations/AbstractOperation_Auxiliaries.cs b/src/UniGetUI.PackageEngine.Operations/AbstractOperation_Auxiliaries.cs
new file mode 100644
index 0000000000..b41d60a12b
--- /dev/null
+++ b/src/UniGetUI.PackageEngine.Operations/AbstractOperation_Auxiliaries.cs
@@ -0,0 +1,100 @@
+namespace UniGetUI.PackageOperations;
+
+public abstract partial class AbstractOperation
+{
+ public static readonly List OperationQueue = [];
+ public static int MAX_OPERATIONS;
+
+ public static class RetryMode
+ {
+ public const string NoRetry = "";
+ public const string Retry = "Retry";
+ public const string Retry_AsAdmin = "RetryAsAdmin";
+ public const string Retry_Interactive = "RetryInteractive";
+ public const string Retry_SkipIntegrity = "RetryNoHashCheck";
+ }
+
+ public class OperationMetadata
+ {
+ ///
+ /// Installation of X
+ ///
+ public string Title = "";
+
+ ///
+ /// X is being installed/upated/removed
+ ///
+ public string Status = "";
+
+ ///
+ /// X was installed
+ ///
+ public string SuccessTitle = "";
+
+ ///
+ /// X has been installed successfully
+ ///
+ public string SuccessMessage = "";
+
+ ///
+ /// X could not be installed.
+ ///
+ public string FailureTitle = "";
+
+ ///
+ /// X Could not be installed
+ ///
+ public string FailureMessage = "";
+
+ ///
+ /// Starting operation X with options Y
+ ///
+ public string OperationInformation = "";
+
+ public readonly string Identifier;
+
+ public OperationMetadata()
+ {
+ Identifier = new Random().NextInt64(1000000, 9999999).ToString();
+ }
+ }
+
+ public class BadgeCollection
+ {
+ public readonly bool AsAdministrator;
+ public readonly bool Interactive;
+ public readonly bool SkipHashCheck;
+ public readonly string? Scope;
+
+ public BadgeCollection(bool admin, bool interactive, bool skiphash, string? scope)
+ {
+ AsAdministrator = admin;
+ Interactive = interactive;
+ SkipHashCheck = skiphash;
+ Scope = scope;
+ }
+ }
+
+ public enum LineType
+ {
+ VerboseDetails,
+ ProgressIndicator,
+ Information,
+ Error
+ }
+
+
+ public struct InnerOperation
+ {
+ public readonly AbstractOperation Operation;
+ public readonly bool MustSucceed;
+
+ public InnerOperation(AbstractOperation op, bool mustSucceed)
+ {
+ Operation = op;
+ MustSucceed = mustSucceed;
+ op.IsInnerOperation = true;
+ }
+ }
+
+}
diff --git a/src/UniGetUI.PackageEngine.Operations/ProcessOperation.cs b/src/UniGetUI.PackageEngine.Operations/AbstractProcessOperation.cs
similarity index 96%
rename from src/UniGetUI.PackageEngine.Operations/ProcessOperation.cs
rename to src/UniGetUI.PackageEngine.Operations/AbstractProcessOperation.cs
index b682969f01..a31d05980e 100644
--- a/src/UniGetUI.PackageEngine.Operations/ProcessOperation.cs
+++ b/src/UniGetUI.PackageEngine.Operations/AbstractProcessOperation.cs
@@ -11,7 +11,10 @@ public abstract class AbstractProcessOperation : AbstractOperation
protected Process process { get; private set; }
private bool ProcessKilled;
- protected AbstractProcessOperation(bool queue_enabled, AbstractOperation? req) : base(queue_enabled, req)
+ protected AbstractProcessOperation(
+ bool queue_enabled,
+ IReadOnlyList? preOps = null,
+ IReadOnlyList? postOps = null) : base(queue_enabled, preOps, postOps)
{
process = new();
CancelRequested += (_, _) =>
diff --git a/src/UniGetUI.PackageEngine.Operations/KillProcessOperation.cs b/src/UniGetUI.PackageEngine.Operations/KillProcessOperation.cs
new file mode 100644
index 0000000000..6f9582f653
--- /dev/null
+++ b/src/UniGetUI.PackageEngine.Operations/KillProcessOperation.cs
@@ -0,0 +1,65 @@
+
+using System.Diagnostics;
+using UniGetUI.Core.SettingsEngine;
+using UniGetUI.Core.Tools;
+using UniGetUI.PackageEngine.Enums;
+
+namespace UniGetUI.PackageOperations;
+
+public class KillProcessOperation: AbstractOperation
+{
+ private string ProcessName;
+ public KillProcessOperation(string procName) : base(false)
+ {
+ ProcessName = CoreTools.MakeValidFileName(procName);
+ Metadata.Status = $"Closing process(es) {procName}";
+ Metadata.Title = $"Closing process(es) {procName}";
+ Metadata.OperationInformation = " ";
+ Metadata.SuccessTitle = $"Done!";
+ Metadata.SuccessMessage = $"Done!";
+ Metadata.FailureTitle = $"Failed to close process";
+ Metadata.FailureMessage = $"The process(es) {procName} could not be closed";
+ }
+
+ protected override void ApplyRetryAction(string retryMode)
+ {
+ }
+
+ protected override async Task PerformOperation()
+ {
+ try
+ {
+ Line($"Attempting to close all processes with name {ProcessName}...", LineType.Information);
+ var procs = Process.GetProcessesByName(ProcessName.Replace(".exe", ""));
+ foreach (var proc in procs)
+ {
+ if(proc.HasExited) continue;
+ Line($"Attempting to close process {ProcessName} with pid={proc.Id}...", LineType.VerboseDetails);
+ proc.CloseMainWindow();
+ await Task.WhenAny(proc.WaitForExitAsync(), Task.Delay(1000));
+ if (!proc.HasExited)
+ {
+ if(Settings.Get(Settings.K.KillProcessesThatRefuseToDie))
+ {
+ Line($"Timeout for process {ProcessName}, attempting to kill...", LineType.Information);
+ proc.Kill();
+ }
+ else
+ {
+ Line($"{ProcessName} with pid={proc.Id} did not exit and will not be killed. You can change this from UniGetUI settings.", LineType.Error);
+ }
+ }
+ }
+
+ return OperationVeredict.Success;
+ }
+ catch (Exception ex)
+ {
+ Line(ex.ToString(), LineType.Error);
+ return OperationVeredict.Failure;
+ }
+ }
+
+ public override Task GetOperationIcon()
+ => Task.FromResult(new Uri("about:blank"));
+}
diff --git a/src/UniGetUI.PackageEngine.Operations/PackageOperations.cs b/src/UniGetUI.PackageEngine.Operations/PackageOperations.cs
index 85baf8c142..a133ff1619 100644
--- a/src/UniGetUI.PackageEngine.Operations/PackageOperations.cs
+++ b/src/UniGetUI.PackageEngine.Operations/PackageOperations.cs
@@ -31,7 +31,7 @@ public PackageOperation(
OperationType role,
bool IgnoreParallelInstalls = false,
AbstractOperation? req = null)
- : base(!IgnoreParallelInstalls, req)
+ : base(!IgnoreParallelInstalls, _getPreInstallOps(options, role, req), _getPostInstallOps(options, role))
{
Package = package;
Options = options;
@@ -128,11 +128,53 @@ public override Task GetOperationIcon()
{
return TaskRecycler.RunOrAttachAsync(Package.GetIconUrl);
}
+
+ private static IReadOnlyList _getPreInstallOps(InstallOptions opts, OperationType role, AbstractOperation? preReq = null)
+ {
+ List l = new();
+ if(preReq is not null) l.Add(new(preReq, true));
+
+ foreach (var process in opts.KillBeforeOperation)
+ l.Add(new InnerOperation(
+ new KillProcessOperation(process),
+ mustSucceed: false));
+
+ if (role is OperationType.Install && opts.PreInstallCommand.Any())
+ l.Add(new(new PrePostOperation(opts.PreInstallCommand), opts.AbortOnPreInstallFail));
+ else if (role is OperationType.Update && opts.PreUpdateCommand.Any())
+ l.Add(new(new PrePostOperation(opts.PreUpdateCommand), opts.AbortOnPreUpdateFail));
+ else if (role is OperationType.Uninstall && opts.PreUninstallCommand.Any())
+ l.Add(new(new PrePostOperation(opts.PreUninstallCommand), opts.AbortOnPreUninstallFail));
+
+ return l;
+ }
+
+ private static IReadOnlyList _getPostInstallOps(InstallOptions opts, OperationType role)
+ {
+ List l = new();
+
+ if (role is OperationType.Install && opts.PostInstallCommand.Any())
+ l.Add(new(new PrePostOperation(opts.PostInstallCommand), false));
+ else if (role is OperationType.Update && opts.PostUpdateCommand.Any())
+ l.Add(new(new PrePostOperation(opts.PostUpdateCommand), false));
+ else if (role is OperationType.Uninstall && opts.PostUninstallCommand.Any())
+ l.Add(new(new PrePostOperation(opts.PostUninstallCommand), false));
+
+ return l;
+ }
}
+ /*
+ *
+ *
+ *
+ * PER-OPERATION PACKAGE OPERATIONS
+ *
+ *
+ *
+ */
public class InstallPackageOperation : PackageOperation
{
-
public InstallPackageOperation(
IPackage package,
InstallOptions options,
diff --git a/src/UniGetUI.PackageEngine.Operations/PrePostOperation.cs b/src/UniGetUI.PackageEngine.Operations/PrePostOperation.cs
new file mode 100644
index 0000000000..d806f4dba0
--- /dev/null
+++ b/src/UniGetUI.PackageEngine.Operations/PrePostOperation.cs
@@ -0,0 +1,63 @@
+using UniGetUI.PackageEngine.Enums;
+using UniGetUI.PackageOperations;
+
+namespace UniGetUI.PackageEngine.Operations;
+
+public class PrePostOperation : AbstractOperation
+{
+ private string Payload;
+ public PrePostOperation(string payload) : base(true)
+ {
+ Payload = payload.Replace("\r", "\n").Replace("\n\n", "\n").Replace("\n", "&");
+ Metadata.Status = $"Running custom operation {Payload}";
+ Metadata.Title = $"Custom operation";
+ Metadata.OperationInformation = " ";
+ Metadata.SuccessTitle = $"Done!";
+ Metadata.SuccessMessage = $"Done!";
+ Metadata.FailureTitle = $"Custom operation failed";
+ Metadata.FailureMessage = $"The custom operation {Payload} failed to run";
+
+ }
+
+ protected override void ApplyRetryAction(string retryMode)
+ {
+ }
+
+ protected override async Task PerformOperation()
+ {
+ Line($"Running command {Payload}", LineType.Information);
+ var process = new System.Diagnostics.Process
+ {
+ StartInfo = new System.Diagnostics.ProcessStartInfo
+ {
+ FileName = "cmd.exe",
+ Arguments = $"/C {Payload}",
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ }
+ };
+
+ process.Start();
+ process.BeginErrorReadLine();
+ process.BeginOutputReadLine();
+ process.OutputDataReceived += (s, e) =>
+ {
+ if (e.Data is not null) Line(e.Data, LineType.Information);
+ };
+ process.ErrorDataReceived += (s, e) =>
+ {
+ if (e.Data is not null) Line(e.Data, LineType.Error);
+ };
+ await process.WaitForExitAsync();
+
+ int exitCode = process.ExitCode;
+ Line($"Exit code is {exitCode}", LineType.Information);
+ return (exitCode == 0 ? OperationVeredict.Success : OperationVeredict.Failure);
+ }
+
+ public override Task GetOperationIcon()
+ => Task.FromResult(new Uri("about:blank"));
+
+}
diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallOptionsFactory.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallOptionsFactory.cs
index f3b4254d7b..871fd8dce2 100644
--- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallOptionsFactory.cs
+++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallOptionsFactory.cs
@@ -223,6 +223,23 @@ private static InstallOptions EnsureSecureOptions(InstallOptions options)
options.CustomParameters_Uninstall = [];
}
+ if (!SecureSettings.Get(SecureSettings.K.AllowPrePostOpCommand))
+ {
+ if (options.PreInstallCommand.Any()) Logger.Warn($"Pre-install command {options.PreInstallCommand} will be discarded");
+ if (options.PostInstallCommand.Any()) Logger.Warn($"Post-install command {options.PostInstallCommand} will be discarded");
+ if (options.PreUpdateCommand.Any()) Logger.Warn($"Pre-update command {options.PreUpdateCommand} will be discarded");
+ if (options.PostUpdateCommand.Any()) Logger.Warn($"Post-update command {options.PostUpdateCommand} will be discarded");
+ if (options.PreUninstallCommand.Any()) Logger.Warn($"Pre-uninstall command {options.PreUninstallCommand} will be discarded");
+ if (options.PostUninstallCommand.Any()) Logger.Warn($"Post-uninstall command {options.PostUninstallCommand} will be discarded");
+
+ options.PreInstallCommand = "";
+ options.PostInstallCommand = "";
+ options.PreUpdateCommand = "";
+ options.PostUpdateCommand = "";
+ options.PreUninstallCommand = "";
+ options.PostUninstallCommand = "";
+ }
+
return options;
}
}
diff --git a/src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs b/src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs
index c5311fed78..d779487782 100644
--- a/src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs
+++ b/src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs
@@ -19,6 +19,18 @@ public class InstallOptions: SerializableComponent
public bool SkipMinorUpdates { get; set; }
public bool OverridesNextLevelOpts { get; set; }
public bool RemoveDataOnUninstall { get; set; }
+ public List KillBeforeOperation { get; set; } = [];
+
+ public string PreInstallCommand { get; set; } = "";
+ public string PostInstallCommand { get; set; } = "";
+ public bool AbortOnPreInstallFail { get; set; } = true;
+ public string PreUpdateCommand { get; set; } = "";
+ public string PostUpdateCommand { get; set; } = "";
+ public bool AbortOnPreUpdateFail { get; set; } = true;
+ public string PreUninstallCommand { get; set; } = "";
+ public string PostUninstallCommand { get; set; } = "";
+ public bool AbortOnPreUninstallFail { get; set; } = true;
+
public override InstallOptions Copy()
{
@@ -27,9 +39,9 @@ public override InstallOptions Copy()
SkipHashCheck = SkipHashCheck,
Architecture = Architecture,
CustomInstallLocation = CustomInstallLocation,
- CustomParameters_Install = CustomParameters_Install,
- CustomParameters_Update = CustomParameters_Update,
- CustomParameters_Uninstall = CustomParameters_Uninstall,
+ CustomParameters_Install = CustomParameters_Install.ToList(),
+ CustomParameters_Update = CustomParameters_Update.ToList(),
+ CustomParameters_Uninstall = CustomParameters_Uninstall.ToList(),
InstallationScope = InstallationScope,
InteractiveInstallation = InteractiveInstallation,
PreRelease = PreRelease,
@@ -38,6 +50,16 @@ public override InstallOptions Copy()
SkipMinorUpdates = SkipMinorUpdates,
OverridesNextLevelOpts = OverridesNextLevelOpts,
RemoveDataOnUninstall = RemoveDataOnUninstall,
+ KillBeforeOperation = KillBeforeOperation.ToList(),
+ PreInstallCommand = PreInstallCommand,
+ PreUpdateCommand = PreUpdateCommand,
+ PreUninstallCommand = PreUninstallCommand,
+ PostInstallCommand = PostInstallCommand,
+ PostUpdateCommand = PostUpdateCommand,
+ PostUninstallCommand = PostUninstallCommand,
+ AbortOnPreInstallFail = AbortOnPreInstallFail,
+ AbortOnPreUpdateFail = AbortOnPreUpdateFail,
+ AbortOnPreUninstallFail = AbortOnPreUninstallFail
};
}
@@ -65,11 +87,22 @@ this.CustomParameters_Uninstall.Count is 0 &&
this.CustomParameters_Uninstall = ReadArrayFromJson(data, "CustomParameters");
}
+ this.KillBeforeOperation = ReadArrayFromJson(data, nameof(KillBeforeOperation));
this.PreRelease = data[nameof(PreRelease)]?.GetVal() ?? false;
this.CustomInstallLocation = data[nameof(CustomInstallLocation)]?.GetVal() ?? "";
this.Version = data[nameof(Version)]?.GetVal() ?? "";
this.SkipMinorUpdates = data[nameof(SkipMinorUpdates)]?.GetVal() ?? false;
+ this.PreInstallCommand = data[nameof(PreInstallCommand)]?.GetVal() ?? "";
+ this.PreUpdateCommand = data[nameof(PreUpdateCommand)]?.GetVal() ?? "";
+ this.PreUninstallCommand = data[nameof(PreUninstallCommand)]?.GetVal() ?? "";
+ this.PostInstallCommand = data[nameof(PostInstallCommand)]?.GetVal() ?? "";
+ this.PostUpdateCommand = data[nameof(PostUpdateCommand)]?.GetVal() ?? "";
+ this.PostUninstallCommand = data[nameof(PostUninstallCommand)]?.GetVal() ?? "";
+ this.AbortOnPreInstallFail = data[nameof(AbortOnPreInstallFail)]?.GetVal() ?? true;
+ this.AbortOnPreUpdateFail = data[nameof(AbortOnPreUpdateFail)]?.GetVal() ?? true;
+ this.AbortOnPreUninstallFail = data[nameof(AbortOnPreUninstallFail)]?.GetVal() ?? true;
+
// if OverridesNextLevelOpts is not found on the JSON, set it to true or false depending
// on whether the current settings instances are different from the default values.
// This entry shall be checked the last one, to ensure all other properties are set
@@ -95,12 +128,22 @@ PreRelease is not false ||
SkipMinorUpdates is not false ||
Architecture.Any() ||
InstallationScope.Any() ||
- CustomParameters_Install.Where(x => x != "").Any() ||
- CustomParameters_Update.Where(x => x != "").Any() ||
- CustomParameters_Uninstall.Where(x => x != "").Any() ||
+ CustomParameters_Install.Where(x => x.Any()).Any() ||
+ CustomParameters_Update.Where(x => x.Any()).Any() ||
+ CustomParameters_Uninstall.Where(x => x.Any()).Any() ||
+ KillBeforeOperation.Where(x => x.Any()).Any() ||
CustomInstallLocation.Any() ||
RemoveDataOnUninstall is not false ||
- Version.Any();
+ Version.Any() ||
+ PreInstallCommand.Any() ||
+ PostInstallCommand.Any() ||
+ AbortOnPreInstallFail is not true ||
+ PreUpdateCommand.Any() ||
+ PostUpdateCommand.Any() ||
+ AbortOnPreUpdateFail is not true ||
+ PreUninstallCommand.Any() ||
+ PostUninstallCommand.Any() ||
+ AbortOnPreUninstallFail is not true;
// OverridesNextLevelOpts does not need to be checked here, since
// this method is invoked before this property has been set
}
@@ -115,8 +158,8 @@ public InstallOptions(JsonNode data) : base(data)
public override string ToString()
{
- string customparams = CustomParameters_Install.Any() ? string.Join(",", CustomParameters_Install) : "[]";
- customparams += CustomParameters_Update.Any() ? string.Join(",", CustomParameters_Update) : "[]";
+ string customparams = CustomParameters_Install.Any() ? string.Join(",", CustomParameters_Install) : "[],";
+ customparams += CustomParameters_Update.Any() ? string.Join(",", CustomParameters_Update) : "[],";
customparams += CustomParameters_Uninstall.Any() ? string.Join(",", CustomParameters_Uninstall) : "[]";
return $"";
}
}
diff --git a/src/UniGetUI/App.xaml.cs b/src/UniGetUI/App.xaml.cs
index 347912e14f..2d2056ba5e 100644
--- a/src/UniGetUI/App.xaml.cs
+++ b/src/UniGetUI/App.xaml.cs
@@ -95,10 +95,6 @@ public MainApp()
private static async void LoadGSudo()
{
-#if DEBUG
- Logger.Warn($"Using bundled GSudo at {CoreData.ElevatorPath} since UniGetUI Elevator is not available!");
- CoreData.ElevatorPath = (await CoreTools.WhichAsync("gsudo.exe")).Item2;
-#else
if (SecureSettings.Get(SecureSettings.K.ForceUserGSudo))
{
var res = await CoreTools.WhichAsync("gsudo.exe");
@@ -110,6 +106,10 @@ private static async void LoadGSudo()
}
}
+#if DEBUG
+ Logger.Warn($"Using bundled GSudo at {CoreData.ElevatorPath} since UniGetUI Elevator is not available!");
+ CoreData.ElevatorPath = (await CoreTools.WhichAsync("gsudo.exe")).Item2;
+#else
CoreData.ElevatorPath = Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "UniGetUI Elevator.exe");
Logger.Debug($"Using built-in UniGetUI Elevator at {CoreData.ElevatorPath}");
#endif
diff --git a/src/UniGetUI/AppOperationHelper.cs b/src/UniGetUI/AppOperationHelper.cs
index 4ef6f908e3..1f8aefb6f4 100644
--- a/src/UniGetUI/AppOperationHelper.cs
+++ b/src/UniGetUI/AppOperationHelper.cs
@@ -116,7 +116,11 @@ public static void Remove(AbstractOperation op)
}
/*
+ *
+ *
* PACKAGE INSTALLATION
+ *
+ *
*/
public static async Task Install(IPackage? package, TEL_InstallReferral referral,
bool? elevated = null, bool? interactive = null, bool? no_integrity = null, bool ignoreParallel = false,
@@ -141,8 +145,31 @@ public static void Install(IReadOnlyList packages, TEL_InstallReferral
}
}
+ public static async Task UninstallThenReinstall(IPackage? package, TEL_InstallReferral referral)
+ {
+ if (package is null) return null;
+
+ var options = await InstallOptionsFactory.LoadApplicableAsync(package);
+
+ var uninstallOp = new UninstallPackageOperation(package, options);
+ uninstallOp.OperationSucceeded += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.SUCCESS);
+ uninstallOp.OperationFailed += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.FAILED);
+
+ var installOp = new InstallPackageOperation(package, options, req: uninstallOp);
+ installOp.OperationSucceeded += (_, _) => TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.SUCCESS, referral);
+ installOp.OperationFailed += (_, _) => TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.FAILED, referral);
+
+ Add(installOp);
+ Instance.MainWindow.UpdateSystemTrayStatus();
+ return installOp;
+ }
+
/*
+ *
+ *
* PACKAGE UPDATE
+ *
+ *
*/
public static async Task Update(IPackage? package, bool? elevated = null, bool? interactive = null, bool? no_integrity = null, bool ignoreParallel = false, AbstractOperation? req = null)
{
@@ -197,8 +224,31 @@ public static async void UpdateForId(string packageId)
Logger.Warn($"[WIDGETS] No package with id={packageId} was found");
}
+ public static async Task UninstallThenUpdate(IPackage? package)
+ {
+ if (package is null) return null;
+
+ var options = await InstallOptionsFactory.LoadApplicableAsync(package);
+
+ var uninstallOp = new UninstallPackageOperation(package, options);
+ uninstallOp.OperationSucceeded += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.SUCCESS);
+ uninstallOp.OperationFailed += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.FAILED);
+
+ var installOp = new UpdatePackageOperation(package, options, req: uninstallOp);
+ installOp.OperationSucceeded += (_, _) => TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.SUCCESS);
+ installOp.OperationFailed += (_, _) => TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.FAILED);
+
+ Add(installOp);
+ Instance.MainWindow.UpdateSystemTrayStatus();
+ return installOp;
+ }
+
/*
+ *
+ *
* PACKAGE UNINSTALL
+ *
+ *
*/
public static async void ConfirmAndUninstall(IReadOnlyList packages, bool? elevated = null, bool? interactive = null, bool? remove_data = null)
diff --git a/src/UniGetUI/Controls/MenuForPackage.cs b/src/UniGetUI/Controls/MenuForPackage.cs
index c45854fa86..3bda72b84d 100644
--- a/src/UniGetUI/Controls/MenuForPackage.cs
+++ b/src/UniGetUI/Controls/MenuForPackage.cs
@@ -76,6 +76,7 @@ public partial class BetterTabViewItem : TabViewItem
public BetterTabViewItem()
{
IsClosable = false;
+ CanDrag = false;
}
public void LoadText()
diff --git a/src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs b/src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs
index 81cd186a8f..f2356409d7 100644
--- a/src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs
+++ b/src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs
@@ -56,6 +56,11 @@ public Brush WarningForeground
set => _warningBlock.Foreground = value;
}
+ public double WarningOpacity
+ {
+ set => _warningBlock.Opacity = value;
+ }
+
public CheckboxCard()
{
_checkbox = new ToggleSwitch()
@@ -74,6 +79,7 @@ public CheckboxCard()
Margin = new Thickness(0, 0, 0, 0),
TextWrapping = TextWrapping.Wrap,
FontSize = 12,
+ Opacity = 0.7,
Visibility = Visibility.Collapsed,
};
IS_INVERTED = false;
diff --git a/src/UniGetUI/CrashHandler.cs b/src/UniGetUI/CrashHandler.cs
index 161ae8bfaf..9f25e0efe2 100644
--- a/src/UniGetUI/CrashHandler.cs
+++ b/src/UniGetUI/CrashHandler.cs
@@ -1,7 +1,5 @@
using System.Diagnostics;
-using Microsoft.UI;
using UniGetUI.Core.Data;
-using UniGetUI.Core.Language;
using UniGetUI.Core.Tools;
namespace UniGetUI;
diff --git a/src/UniGetUI/EntryPoint.cs b/src/UniGetUI/EntryPoint.cs
index 20514ed893..37f88c4db8 100644
--- a/src/UniGetUI/EntryPoint.cs
+++ b/src/UniGetUI/EntryPoint.cs
@@ -3,7 +3,6 @@
using Microsoft.Windows.AppLifecycle;
using UniGetUI.Core.Data;
using UniGetUI.Core.Logging;
-using UniGetUI.Core.Tools;
namespace UniGetUI
{
diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs b/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs
index deeef6fd16..29c068ce43 100644
--- a/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs
+++ b/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs
@@ -1,7 +1,3 @@
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using CommunityToolkit.WinUI.Controls;
-using Microsoft.Extensions.Options;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using UniGetUI.Core.Language;
diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml b/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml
index ea6922d21d..d735d0d865 100644
--- a/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml
+++ b/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml
@@ -3,6 +3,7 @@
x:Class="UniGetUI.Interface.Dialogs.InstallOptionsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:animations="using:CommunityToolkit.WinUI.Animations"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:UniGetUI.Interface.Dialogs"
@@ -11,6 +12,16 @@
xmlns:widgets="using:UniGetUI.Interface.Widgets"
MaxWidth="700"
mc:Ignorable="d">
+
+
+
+
+
+
+
+
+
+
@@ -130,7 +141,9 @@
x:Name="SettingsTabBar"
HorizontalAlignment="Stretch"
Background="{ThemeResource SystemChromeAltHighColor}"
+ CanReorderTabs="False"
IsAddTabButtonVisible="False"
+ SelectionChanged="SettingsTabBar_SelectionChanged"
TabWidthMode="SizeToContent">
-
+ Line2="Post-install" />
@@ -165,6 +180,16 @@
+
+
+
+
@@ -176,7 +201,10 @@
-
+
@@ -217,7 +245,17 @@
-
+
+
+
+
+
@@ -234,7 +272,10 @@
SelectedIndex="0"
SelectionChanged="ArchitectureComboBox_SelectionChanged" />
-
+
@@ -250,7 +291,7 @@
VerticalAlignment="Center"
SelectionChanged="ScopeCombo_SelectionChanged" />
-
+
@@ -291,9 +332,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -405,6 +659,7 @@
ProcessesToKill = new();
+ private readonly ObservableCollection _runningProcesses = new();
+ public ObservableCollection SuggestedProcesses = new();
+
public InstallOptionsPage(IPackage package, InstallOptions options) : this(package, OperationType.None, options) { }
public InstallOptionsPage(IPackage package, OperationType operation, InstallOptions options)
{
@@ -40,6 +44,8 @@ public InstallOptionsPage(IPackage package, OperationType operation, InstallOpti
Operation = operation;
Options = options;
+ KillProcessesThatWontDie.IsChecked = Settings.Get(Settings.K.KillProcessesThatRefuseToDie);
+
ProfileComboBox.Items.Add(CoreTools.Translate("Install"));
ProfileComboBox.Items.Add(CoreTools.Translate("Update"));
ProfileComboBox.Items.Add(CoreTools.Translate("Uninstall"));
@@ -83,6 +89,9 @@ async Task LoadImage()
DialogTitle.Text = CoreTools.Translate("{0} installation options", package.Name);
PlaceholderText.Text = CoreTools.Translate("{0} Install options are currently locked because {0} follows the default install options.", package.Name);
+ KillProcessesLabel.Text = CoreTools.Translate("Select the processes that should be closed before this package is installed, updated or uninstalled.");
+ KillProcessesBox.PlaceholderText = CoreTools.Translate("Write here the process names here, separed by commas (,)");
+
packageInstallLocation = Package.Manager.DetailsHelper.GetInstallLocation(package) ?? CoreTools.Translate("Unset or unknown");
AdminCheckBox.IsChecked = Options.RunAsAdministrator;
@@ -154,20 +163,45 @@ async Task LoadImage()
}
}
+ foreach(var p in Options.KillBeforeOperation)
+ {
+ ProcessesToKill.Add(new(p));
+ }
if (Options.CustomInstallLocation == "") CustomInstallLocation.Text = packageInstallLocation;
else CustomInstallLocation.Text = Options.CustomInstallLocation;
-
CustomParameters1.Text = string.Join(' ', Options.CustomParameters_Install);
CustomParameters2.Text = string.Join(' ', Options.CustomParameters_Update);
CustomParameters3.Text = string.Join(' ', Options.CustomParameters_Uninstall);
+ PreInstallCommandBox.Text = Options.PreInstallCommand;
+ PostInstallCommandBox.Text = Options.PostInstallCommand;
+ PreUpdateCommandBox.Text = Options.PreUpdateCommand;
+ PostUpdateCommandBox.Text = Options.PostUpdateCommand;
+ PreUninstallCommandBox.Text = Options.PreUninstallCommand;
+ PostUninstallCommandBox.Text = Options.PostUninstallCommand;
+ AbortInsFailedCheck.IsChecked = Options.AbortOnPreInstallFail;
+ AbortUpdFailedCheck.IsChecked = Options.AbortOnPreUpdateFail;
+ AbortUniFailedCheck.IsChecked = Options.AbortOnPreUninstallFail;
+
_uiLoaded = true;
EnableDisableControls(operation);
LoadIgnoredUpdates();
+ _ = _loadProcesses();
}
+ private async Task _loadProcesses()
+ {
+ var processNames = await Task.Run(() =>
+ Process.GetProcesses().Select(p => p.ProcessName).Distinct().ToList());
+
+ _runningProcesses.Clear();
+ foreach (var name in processNames)
+ {
+ if(name.Any()) _runningProcesses.Add(new(name + ".exe"));
+ }
+ }
private void EnableDisableControls(OperationType operation)
{
if(FollowGlobalOptionsSwitch.IsOn)
@@ -188,13 +222,17 @@ private void EnableDisableControls(OperationType operation)
AdminCheckBox.IsEnabled = Package.Manager.Capabilities.CanRunAsAdmin;
InteractiveCheckBox.IsEnabled = Package.Manager.Capabilities.CanRunInteractively;
- HashCheckbox.IsEnabled = operation != OperationType.Uninstall && Package.Manager.Capabilities.CanSkipIntegrityChecks;
- ArchitectureComboBox.IsEnabled = operation != OperationType.Uninstall && Package.Manager.Capabilities.SupportsCustomArchitectures;
+ HashCheckbox.IsEnabled =
+ operation is not OperationType.Uninstall
+ && Package.Manager.Capabilities.CanSkipIntegrityChecks;
+
+ ArchitectureComboBox.IsEnabled =
+ operation is not OperationType.Uninstall
+ && Package.Manager.Capabilities.SupportsCustomArchitectures;
+
VersionComboBox.IsEnabled =
- (operation == OperationType.Install
- || operation == OperationType.None)
- && (Package.Manager.Capabilities.SupportsCustomVersions
- || Package.Manager.Capabilities.SupportsPreRelease);
+ operation is OperationType.Install or OperationType.None
+ && (Package.Manager.Capabilities.SupportsCustomVersions || Package.Manager.Capabilities.SupportsPreRelease);
ScopeCombo.IsEnabled = Package.Manager.Capabilities.SupportsCustomScopes;
ResetDir.IsEnabled = Package.Manager.Capabilities.SupportsCustomLocations;
SelectDir.IsEnabled = Package.Manager.Capabilities.SupportsCustomLocations;
@@ -209,6 +247,27 @@ private void EnableDisableControls(OperationType operation)
CustomParametersLabel3.Opacity = IsCLIEnabled ? 1 : 0.5;
GoToCLISettings.Visibility = IsCLIEnabled ? Visibility.Collapsed : Visibility.Visible;
CLIDisabled.Visibility = IsCLIEnabled ? Visibility.Collapsed : Visibility.Visible;
+
+ bool IsPrePostOpEnabled = SecureSettings.Get(SecureSettings.K.AllowPrePostOpCommand);
+ PreInstallCommandBox.IsEnabled = IsPrePostOpEnabled;
+ PostInstallCommandBox.IsEnabled = IsPrePostOpEnabled;
+ AbortInsFailedCheck.IsEnabled = IsPrePostOpEnabled;
+ PreUpdateCommandBox.IsEnabled = IsPrePostOpEnabled;
+ PostUpdateCommandBox.IsEnabled = IsPrePostOpEnabled;
+ AbortUpdFailedCheck.IsEnabled = IsPrePostOpEnabled;
+ PreUninstallCommandBox.IsEnabled = IsPrePostOpEnabled;
+ PostUninstallCommandBox.IsEnabled = IsPrePostOpEnabled;
+ AbortUniFailedCheck.IsEnabled = IsPrePostOpEnabled;
+ PeInsLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5;
+ PoInsLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5;
+ PeUpdLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5;
+ PoUpdLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5;
+ PeUniLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5;
+ PoUniLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5;
+ CustomCommandsHeaderExplainer.Opacity = IsPrePostOpEnabled ? 1 : 0.5;
+ GoToPrePostSettings.Visibility = IsPrePostOpEnabled ? Visibility.Collapsed : Visibility.Visible;
+ PrePostDisabled.Visibility = IsPrePostOpEnabled ? Visibility.Collapsed : Visibility.Visible;
+
GenerateCommand();
}
@@ -233,14 +292,12 @@ private async Task LoadVersions()
}
VersionComboBox.IsEnabled =
- (Operation == OperationType.Install
- || Operation == OperationType.None)
- && (Package.Manager.Capabilities.SupportsCustomVersions
- || Package.Manager.Capabilities.SupportsPreRelease);
+ Operation is OperationType.Install or OperationType.None
+ && (Package.Manager.Capabilities.SupportsCustomVersions || Package.Manager.Capabilities.SupportsPreRelease);
VersionProgress.Visibility = Visibility.Collapsed;
}
- public async Task GetUpdatedOptions(bool updateIgnoredUpdates = true)
+ public async Task GetUpdatedOptions(bool updateDetachedOptions = true)
{
Options.RunAsAdministrator = AdminCheckBox?.IsChecked ?? false;
Options.InteractiveInstallation = InteractiveCheckBox?.IsChecked ?? false;
@@ -269,6 +326,19 @@ public async Task GetUpdatedOptions(bool updateIgnoredUpdates =
Options.CustomParameters_Uninstall = CustomParameters3.Text.Split(' ').ToList();
Options.PreRelease = VersionComboBox.SelectedValue.ToString() == CoreTools.Translate("PreRelease");
+ Options.PreInstallCommand = PreInstallCommandBox.Text;
+ Options.PostInstallCommand = PostInstallCommandBox.Text;
+ Options.PreUpdateCommand = PreUpdateCommandBox.Text;
+ Options.PostUpdateCommand = PostUpdateCommandBox.Text;
+ Options.PreUninstallCommand = PreUninstallCommandBox.Text;
+ Options.PostUninstallCommand = PostUninstallCommandBox.Text;
+ Options.AbortOnPreInstallFail = AbortInsFailedCheck.IsChecked ?? true;
+ Options.AbortOnPreUpdateFail = AbortUpdFailedCheck.IsChecked ?? true;
+ Options.AbortOnPreUninstallFail = AbortUniFailedCheck.IsChecked ?? true;
+
+ Options.KillBeforeOperation.Clear();
+ foreach(var p in ProcessesToKill) Options.KillBeforeOperation.Add(p.Name);
+
if (VersionComboBox.SelectedValue.ToString() != CoreTools.Translate("PreRelease") && VersionComboBox.SelectedValue.ToString() != CoreTools.Translate("Latest"))
{
Options.Version = VersionComboBox.SelectedValue.ToString() ?? "";
@@ -279,8 +349,10 @@ public async Task GetUpdatedOptions(bool updateIgnoredUpdates =
}
Options.SkipMinorUpdates = SkipMinorUpdatesCheckbox?.IsChecked ?? false;
- if (updateIgnoredUpdates)
+ if (updateDetachedOptions)
{
+ Settings.Set(Settings.K.KillProcessesThatRefuseToDie, KillProcessesThatWontDie.IsChecked ?? false);
+
if (IgnoreUpdatesCheckbox?.IsChecked ?? false)
{
await Package.AddToIgnoredUpdatesAsync(version: "*");
@@ -329,7 +401,7 @@ private void CloseButton_Click(object sender, RoutedEventArgs e)
private async void GenerateCommand()
{
if (!_uiLoaded) return;
- InstallOptions options = await GetUpdatedOptions(updateIgnoredUpdates: false);
+ InstallOptions options = await GetUpdatedOptions(updateDetachedOptions: false);
options = await InstallOptionsFactory.LoadApplicableAsync(this.Package, overridePackageOptions: options);
var op = ProfileComboBox.SelectedIndex switch
@@ -363,5 +435,50 @@ private void GoToSecureSettings_Click(object sender, RoutedEventArgs e)
Close?.Invoke(this, EventArgs.Empty);
MainApp.Instance.MainWindow.NavigationPage.OpenSettingsPage(typeof(Administrator));
}
+
+ private void KillProcessesBox_TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
+ {
+ args.Item = _runningProcesses.FirstOrDefault((item) => item.Name.Contains(args.TokenText));
+ if(args.Item is null)
+ {
+ string text = args.TokenText;
+ if (!text.EndsWith(".exe")) text += ".exe";
+ args.Item = new IOP_Proc(text);
+ }
+ }
+
+ private async void KillProcessesBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
+ {
+ var text = KillProcessesBox.Text;
+ await Task.Delay(100);
+ if (text != KillProcessesBox.Text)
+ return;
+
+ SuggestedProcesses.Clear();
+ if (text.Trim() != "")
+ {
+ if (!text.EndsWith(".exe"))
+ text = text.Trim() + ".exe";
+ SuggestedProcesses.Add(new(text));
+ foreach (var item in _runningProcesses.Where(x => x.Name.Contains(KillProcessesBox.Text)))
+ {
+ SuggestedProcesses.Add(item);
+ }
+ }
+ }
+
+ private void SettingsTabBar_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ CommandLineViewBox.Visibility = SettingsTabBar.SelectedIndex < 3 ? Visibility.Visible : Visibility.Collapsed;
+ }
+ }
+
+ public class IOP_Proc
+ {
+ public readonly string Name;
+ public IOP_Proc(string name)
+ {
+ Name = name;
+ }
}
}
diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml
index f2af768643..6128421e16 100644
--- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml
+++ b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml
@@ -65,15 +65,13 @@
Text="Allow custom command-line arguments"
WarningText="Custom command-line arguments can change the way in which programs are installed, upgraded or uninstalled, in a way UniGetUI cannot control. Using custom command-lines can break packages. Proceed with caution." />
-
+ WarningText="Pre and post install commands will be run before and after a package gets installed, upgraded or uninstalled. Be aware that they may break things unless used carefully" />
-
+ WarningText="Pre and post install commands can do very nasty things to your device, if designed to do so. It can be very dangerous to import the commands from a bundle, unless you trust the source of that package bundle" />
diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml
index 8d0ff4d959..493123dd08 100644
--- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml
+++ b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml
@@ -40,6 +40,15 @@
SettingName="MaintainSuccessfulInstalls"
Text="Clear successful operations from the operation list after a 5 second delay" />
+
+
+
_ = MainApp.Operations.Install(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED);
- private async void MenuUninstallThenReinstall_Invoked(object sender, RoutedEventArgs args)
- {
- var op = await MainApp.Operations.Uninstall(SelectedItem, ignoreParallel: true);
- _ = MainApp.Operations.Install(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED, ignoreParallel: true, req: op);
- }
+ private void MenuUninstallThenReinstall_Invoked(object sender, RoutedEventArgs args)
+ => _ = MainApp.Operations.UninstallThenReinstall(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED);
private async void MenuIgnorePackage_Invoked(object sender, RoutedEventArgs args)
{
diff --git a/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs b/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs
index 712128aab4..d8d7411db9 100644
--- a/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs
+++ b/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs
@@ -1,8 +1,6 @@
using System.Diagnostics;
-using System.Runtime.Serialization.Formatters;
using System.Text.Json;
using System.Text.Json.Nodes;
-using System.Xml;
using System.Xml.Serialization;
using ExternalLibraries.Pickers;
using Microsoft.UI.Text;
@@ -665,46 +663,81 @@ public async Task AddFromBundle(string content, BundleFormatType format)
SecureSettings.Get(SecureSettings.K.AllowCLIArguments) &&
SecureSettings.Get(SecureSettings.K.AllowImportingCLIArguments);
+ bool AllowPrePostOps =
+ SecureSettings.Get(SecureSettings.K.AllowPrePostOpCommand) &&
+ SecureSettings.Get(SecureSettings.K.AllowImportPrePostOpCommands);
+
foreach (var pkg in DeserializedData.packages)
{
- if (pkg.InstallationOptions.CustomParameters_Install.Where(x => x.Any()).Any())
+ var opts = pkg.InstallationOptions;
+
+ if (opts.CustomParameters_Install.Where(x => x.Any()).Any())
{
showReport = true;
- if (!packageReport.ContainsKey(pkg.Id))
- packageReport[pkg.Id] = new();
-
- packageReport[pkg.Id].Add(new(
- $"Custom install arguments: [{string.Join(", ", pkg.InstallationOptions.CustomParameters_Install)}]",
- AllowCLIParameters));
-
- if(!AllowCLIParameters) pkg.InstallationOptions.CustomParameters_Install.Clear();
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Custom install arguments: [{string.Join(", ", opts.CustomParameters_Install)}]", AllowCLIParameters));
+ if(!AllowCLIParameters) opts.CustomParameters_Install.Clear();
}
- if (pkg.InstallationOptions.CustomParameters_Update.Where(x => x.Any()).Any())
+ if (opts.CustomParameters_Update.Where(x => x.Any()).Any())
{
showReport = true;
- if (!packageReport.ContainsKey(pkg.Id))
- packageReport[pkg.Id] = new();
-
- packageReport[pkg.Id].Add(new(
- $"Custom update arguments: [{string.Join(", ", pkg.InstallationOptions.CustomParameters_Update)}]",
- AllowCLIParameters));
-
- if(!AllowCLIParameters) pkg.InstallationOptions.CustomParameters_Update.Clear();
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Custom update arguments: [{string.Join(", ", opts.CustomParameters_Update)}]", AllowCLIParameters));
+ if(!AllowCLIParameters) opts.CustomParameters_Update.Clear();
}
- if (pkg.InstallationOptions.CustomParameters_Uninstall.Where(x => x.Any()).Any())
+ if (opts.CustomParameters_Uninstall.Where(x => x.Any()).Any())
{
showReport = true;
- if (!packageReport.ContainsKey(pkg.Id))
- packageReport[pkg.Id] = new();
-
- packageReport[pkg.Id].Add(new(
- $"Custom uninstall arguments: [{string.Join(", ", pkg.InstallationOptions.CustomParameters_Uninstall)}]",
- AllowCLIParameters));
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Custom uninstall arguments: [{string.Join(", ", opts.CustomParameters_Uninstall)}]", AllowCLIParameters));
+ if(!AllowCLIParameters) opts.CustomParameters_Uninstall.Clear();
+ }
- if(!AllowCLIParameters) pkg.InstallationOptions.CustomParameters_Uninstall.Clear();
+ if (opts.PreInstallCommand.Any())
+ {
+ showReport = true;
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Pre-install command: {opts.PreInstallCommand}", AllowPrePostOps));
+ if (!AllowPrePostOps) opts.PreInstallCommand = "";
+ }
+ if (opts.PostInstallCommand.Any())
+ {
+ showReport = true;
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Post-install command: {opts.PostInstallCommand}", AllowPrePostOps));
+ if (!AllowPrePostOps) opts.PostInstallCommand = "";
+ }
+ if (opts.PreUpdateCommand.Any())
+ {
+ showReport = true;
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Pre-update command: {opts.PreUpdateCommand}", AllowPrePostOps));
+ if (!AllowPrePostOps) opts.PreUpdateCommand = "";
+ }
+ if (opts.PostUpdateCommand.Any())
+ {
+ showReport = true;
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Post-update command: {opts.PostUpdateCommand}", AllowPrePostOps));
+ if (!AllowPrePostOps) opts.PostUpdateCommand = "";
+ }
+ if (opts.PreUninstallCommand.Any())
+ {
+ showReport = true;
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Pre-uninstall command: {opts.PreUninstallCommand}", AllowPrePostOps));
+ if (!AllowPrePostOps) opts.PreUninstallCommand = "";
+ }
+ if (opts.PostUninstallCommand.Any())
+ {
+ showReport = true;
+ if (!packageReport.ContainsKey(pkg.Id)) packageReport[pkg.Id] = new();
+ packageReport[pkg.Id].Add(new($"Post-uninstall command: {opts.PostUninstallCommand}", AllowPrePostOps));
+ if (!AllowPrePostOps) opts.PostUninstallCommand = "";
}
+ pkg.InstallationOptions = opts;
packages.Add(DeserializePackage(pkg));
}
diff --git a/src/UniGetUI/Pages/SoftwarePages/SoftwareUpdatesPage.cs b/src/UniGetUI/Pages/SoftwarePages/SoftwareUpdatesPage.cs
index eb3f502af3..49e6263fa4 100644
--- a/src/UniGetUI/Pages/SoftwarePages/SoftwareUpdatesPage.cs
+++ b/src/UniGetUI/Pages/SoftwarePages/SoftwareUpdatesPage.cs
@@ -494,11 +494,8 @@ private void MenuInteractive_Invoked(object sender, RoutedEventArgs e)
private void MenuAsAdmin_Invoked(object sender, RoutedEventArgs e)
=> _ = MainApp.Operations.Update(SelectedItem, elevated: true);
- private async void MenuUpdateAfterUninstall_Invoked(object sender, RoutedEventArgs e)
- {
- var op = await MainApp.Operations.Uninstall(SelectedItem);
- _ = MainApp.Operations.Install(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED, req: op);
- }
+ private void MenuUpdateAfterUninstall_Invoked(object sender, RoutedEventArgs e)
+ => _ = MainApp.Operations.UninstallThenUpdate(SelectedItem);
private void MenuUninstall_Invoked(object sender, RoutedEventArgs e)
=> _ = MainApp.Operations.Uninstall(SelectedItem);
diff --git a/src/UniGetUI/UniGetUI.csproj b/src/UniGetUI/UniGetUI.csproj
index 9a3c1c3188..8da2b149ac 100644
--- a/src/UniGetUI/UniGetUI.csproj
+++ b/src/UniGetUI/UniGetUI.csproj
@@ -73,6 +73,7 @@
+