Skip to content

Commit 7f43cf5

Browse files
authored
Merge pull request #3703 from mrixner/select-executables
2 parents 76e7f37 + 6b5230f commit 7f43cf5

39 files changed

Lines changed: 526 additions & 230 deletions

File tree

src/UniGetUI.Core.Data/CoreData.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,5 +390,6 @@ private static int GetCodePage()
390390
}
391391
}
392392

393+
public static readonly string PowerShell5 = Path.Join(Environment.SystemDirectory, "windowspowershell\\v1.0\\powershell.exe");
393394
}
394395
}

src/UniGetUI.Core.SecureSettings/SecureSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public enum K
1414
AllowPrePostOpCommand,
1515
AllowImportPrePostOpCommands,
1616
ForceUserGSudo,
17+
AllowCustomManagerPaths,
1718
Unset
1819
};
1920

@@ -26,6 +27,7 @@ public static string ResolveKey(K key)
2627
K.AllowPrePostOpCommand => "AllowPrePostInstallCommands",
2728
K.AllowImportPrePostOpCommands => "AllowImportingPrePostInstallCommands",
2829
K.ForceUserGSudo => "ForceUserGSudo",
30+
K.AllowCustomManagerPaths => "AllowCustomManagerPaths",
2931

3032
K.Unset => throw new InvalidDataException("SecureSettings key was unset!"),
3133
_ => throw new KeyNotFoundException($"The SecureSettings key {key} was not found on the ResolveKey map")

src/UniGetUI.Core.Settings/SettingsEngine_Names.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public enum K
7575
DisableSuccessNotifications,
7676
DisableProgressNotifications,
7777
KillProcessesThatRefuseToDie,
78+
ManagerPaths,
7879

7980
Test1,
8081
Test2,
@@ -161,6 +162,7 @@ public static string ResolveKey(K key)
161162
K.DisableSuccessNotifications => "DisableSuccessNotifications",
162163
K.DisableProgressNotifications => "DisableProgressNotifications",
163164
K.KillProcessesThatRefuseToDie => "KillProcessesThatRefuseToDie",
165+
K.ManagerPaths => "ManagerPaths",
164166

165167
K.Test1 => "TestSetting1",
166168
K.Test2 => "TestSetting2",

src/UniGetUI.Core.Tools/Tools.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ public static void RelaunchProcess()
103103
/// Finds an executable in path and returns its location
104104
/// </summary>
105105
/// <param name="command">The executable alias to find</param>
106-
/// <returns>A tuple containing: a boolean hat represents whether the path was found or not; the path to the file if found.</returns>
106+
/// <returns>A tuple containing: a boolean that represents whether the path was found or not; the path to the file if found.</returns>
107107
public static async Task<Tuple<bool, string>> WhichAsync(string command)
108108
{
109109
return await Task.Run(() => Which(command));
110110
}
111111

112-
public static Tuple<bool, string> Which(string command, bool updateEnv = true)
112+
public static List<string> WhichMultiple(string command, bool updateEnv = true)
113113
{
114114
command = command.Replace(";", "").Replace("&", "").Trim();
115115
Logger.Debug($"Begin \"which\" search for command {command}");
@@ -142,30 +142,36 @@ public static Tuple<bool, string> Which(string command, bool updateEnv = true)
142142
try
143143
{
144144
process.Start();
145-
string? line = process.StandardOutput.ReadLine();
146-
string output;
147-
148-
if (line is null) output = "";
149-
else output = line.Trim();
145+
string[] lines = process.StandardOutput.ReadToEnd()
146+
.Split(["\r\n", "\n"], StringSplitOptions.RemoveEmptyEntries);
150147

151148
process.WaitForExit();
152149

153-
if (process.ExitCode != 0 || output == "")
150+
if (process.ExitCode is not 0)
151+
Logger.Warn($"Call to WhichMultiple with file {command} returned non-zero status {process.ExitCode}");
152+
153+
if (lines.Length is 0)
154154
{
155155
Logger.ImportantInfo($"Command {command} was not found on the system");
156-
return new Tuple<bool, string>(false, "");
156+
return [];
157157
}
158158

159-
Logger.Debug($"Command {command} was found on {output}");
160-
return new Tuple<bool, string>(File.Exists(output), output);
159+
Logger.Debug($"Command {command} was found on {lines[0]} (with {lines.Length-1} more occurrences)");
160+
return lines.ToList();
161161
}
162162
catch
163163
{
164-
if (updateEnv) return Which(command, false);
164+
if (updateEnv) return WhichMultiple(command, false);
165165
throw;
166166
}
167167
}
168168

169+
public static Tuple<bool, string> Which(string command, bool updateEnv = true)
170+
{
171+
var paths = WhichMultiple(command, updateEnv);
172+
return new(paths.Any(), paths.Any() ? paths[0]: "");
173+
}
174+
169175
/// <summary>
170176
/// Formats a given package id as a name, capitalizing words and replacing separators with spaces
171177
/// </summary>

src/UniGetUI.PAckageEngine.Interfaces/IPackageManager.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,13 @@ public interface IPackageManager
6868
/// an example
6969
/// </summary>
7070
public void AttemptFastRepair();
71+
72+
/// <summary>
73+
/// Find all available executable files that apply to this package manager
74+
/// </summary>
75+
/// <returns></returns>
76+
public IReadOnlyList<string> FindCandidateExecutableFiles();
77+
public Tuple<bool, string> GetExecutableFile();
78+
7179
}
7280
}

src/UniGetUI.PAckageEngine.Interfaces/ManagerProperties.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public struct ManagerProperties
1111
public string Description { get; set; } = "Unset";
1212
public IconType IconId { get; set; } = IconType.Help;
1313
public string ColorIconId { get; set; } = "Unset";
14-
public string ExecutableCallArgs { get; set; } = "Unset";
14+
// public string ExecutableCallArgs { get; set; } = "Unset";
1515
public string ExecutableFriendlyName { get; set; } = "Unset";
1616
public string InstallVerb { get; set; } = "Unset";
1717
public string UpdateVerb { get; set; } = "Unset";

src/UniGetUI.PackageEngine.Enums/ManagerStatus.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ public struct ManagerStatus
55
public string Version = "";
66
public bool Found = false;
77
public string ExecutablePath = "";
8+
public string ExecutableCallArgs { get; set; } = "Unset";
89
public ManagerStatus()
910
{ }
1011
}

src/UniGetUI.PackageEngine.Managers.Cargo/Cargo.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Text;
33
using System.Text.RegularExpressions;
44
using UniGetUI.Core.Classes;
5+
using UniGetUI.Core.Data;
56
using UniGetUI.Core.Logging;
67
using UniGetUI.Core.Tools;
78
using UniGetUI.PackageEngine.Classes.Manager;
@@ -29,14 +30,14 @@ public Cargo()
2930
// cargo-update is required to check for installed and upgradable packages
3031
new ManagerDependency(
3132
"cargo-update",
32-
Path.Join(Environment.SystemDirectory, "windowspowershell\\v1.0\\powershell.exe"),
33+
CoreData.PowerShell5,
3334
"-ExecutionPolicy Bypass -NoLogo -NoProfile -Command \"& {cargo install cargo-update; if ($error.count -ne 0){pause}}\"",
3435
"cargo install cargo-update",
3536
async () => (await CoreTools.WhichAsync("cargo-install-update.exe")).Item1),
3637
// Cargo-binstall is required to install and update cargo binaries
3738
new ManagerDependency(
3839
"cargo-binstall",
39-
Path.Join(Environment.SystemDirectory, "windowspowershell\\v1.0\\powershell.exe"),
40+
CoreData.PowerShell5,
4041
"-ExecutionPolicy Bypass -NoLogo -NoProfile -Command \"& {Set-ExecutionPolicy Unrestricted -Scope Process; iex (iwr \\\"https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.ps1\\\").Content; if ($error.count -ne 0){pause}}\"",
4142
"Set-ExecutionPolicy Unrestricted -Scope Process; iex (iwr \"https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.ps1\").Content",
4243
async () => (await CoreTools.WhichAsync("cargo-binstall.exe")).Item1)
@@ -64,7 +65,6 @@ public Cargo()
6465
InstallVerb = "binstall",
6566
UninstallVerb = "uninstall",
6667
UpdateVerb = "binstall",
67-
ExecutableCallArgs = "",
6868
DefaultSource = cratesIo,
6969
KnownSources = [cratesIo]
7070
};
@@ -136,9 +136,14 @@ protected override IReadOnlyList<Package> GetInstalledPackages_UnSafe()
136136
return GetPackages(LoggableTaskType.ListInstalledPackages);
137137
}
138138

139+
public override IReadOnlyList<string> FindCandidateExecutableFiles()
140+
{
141+
return CoreTools.WhichMultiple("cargo.exe");
142+
}
143+
139144
protected override ManagerStatus LoadManager()
140145
{
141-
var (found, executablePath) = CoreTools.Which("cargo");
146+
var (found, executablePath) = GetExecutableFile();
142147
if (!found)
143148
{
144149
return new(){ ExecutablePath = executablePath, Found = false, Version = ""};
@@ -153,7 +158,7 @@ protected override ManagerStatus LoadManager()
153158
Logger.Error("cargo version error: " + error);
154159
}
155160

156-
return new() { ExecutablePath = executablePath, Found = found, Version = version };
161+
return new() { ExecutablePath = executablePath, Found = found, Version = version, ExecutableCallArgs = ""};
157162
}
158163

159164
private IReadOnlyList<Package> GetPackages(LoggableTaskType taskType)
@@ -204,7 +209,7 @@ private Process GetProcess(string fileName, string extraArguments)
204209
StartInfo = new ProcessStartInfo
205210
{
206211
FileName = fileName,
207-
Arguments = Properties.ExecutableCallArgs + " " + extraArguments,
212+
Arguments = Status.ExecutableCallArgs + " " + extraArguments,
208213
RedirectStandardOutput = true,
209214
RedirectStandardError = true,
210215
UseShellExecute = false,

src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ namespace UniGetUI.PackageEngine.Managers.ChocolateyManager
1818
{
1919
public class Chocolatey : BaseNuGet
2020
{
21-
public static new string[] FALSE_PACKAGE_IDS = ["Directory", "", "Did", "Features?", "Validation", "-", "being", "It", "Error", "L'accs", "Maximum", "This", "Output is package name ", "operable", "Invalid"];
22-
public static new string[] FALSE_PACKAGE_VERSIONS = ["", "of", "Did", "Features?", "Validation", "-", "being", "It", "Error", "L'accs", "Maximum", "This", "packages", "current version", "installed version", "is", "program", "validations", "argument", "no"];
21+
public static readonly string[] FALSE_PACKAGE_IDS = ["Directory", "", "Did", "Features?", "Validation", "-", "being", "It", "Error", "L'accs", "Maximum", "This", "Output is package name ", "operable", "Invalid"];
22+
public static readonly string[] FALSE_PACKAGE_VERSIONS = ["", "of", "Did", "Features?", "Validation", "-", "being", "It", "Error", "L'accs", "Maximum", "This", "packages", "current version", "installed version", "is", "program", "validations", "argument", "no"];
23+
private static readonly string OldChocoPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs\\WingetUI\\choco-cli");
24+
private static readonly string NewChocoPath = Path.Join(CoreData.UniGetUIDataDirectory, "Chocolatey");
2325

2426
public Chocolatey()
2527
{
@@ -54,7 +56,6 @@ public Chocolatey()
5456
InstallVerb = "install",
5557
UninstallVerb = "uninstall",
5658
UpdateVerb = "upgrade",
57-
ExecutableCallArgs = "",
5859
KnownSources = [new ManagerSource(this, "community", new Uri("https://community.chocolatey.org/api/v2/"))],
5960
DefaultSource = new ManagerSource(this, "community", new Uri("https://community.chocolatey.org/api/v2/")),
6061

@@ -75,7 +76,7 @@ public static string GetProxyArgument()
7576
return $"--proxy {proxyUri.ToString()}";
7677

7778
var creds = Settings.GetProxyCredentials();
78-
if(creds is null)
79+
if (creds is null)
7980
return $"--proxy {proxyUri.ToString()}";
8081

8182
return $"--proxy={proxyUri.ToString()} --proxy-user={Uri.EscapeDataString(creds.UserName)}" +
@@ -89,7 +90,7 @@ protected override IReadOnlyList<Package> GetAvailableUpdates_UnSafe()
8990
StartInfo = new ProcessStartInfo
9091
{
9192
FileName = Status.ExecutablePath,
92-
Arguments = Properties.ExecutableCallArgs + " outdated " + GetProxyArgument(),
93+
Arguments = Status.ExecutableCallArgs + " outdated " + GetProxyArgument(),
9394
RedirectStandardOutput = true,
9495
RedirectStandardError = true,
9596
RedirectStandardInput = true,
@@ -143,7 +144,7 @@ protected override IReadOnlyList<Package> _getInstalledPackages_UnSafe()
143144
StartInfo = new ProcessStartInfo
144145
{
145146
FileName = Status.ExecutablePath,
146-
Arguments = Properties.ExecutableCallArgs + " list " + GetProxyArgument(),
147+
Arguments = Status.ExecutableCallArgs + " list " + GetProxyArgument(),
147148
RedirectStandardOutput = true,
148149
RedirectStandardError = true,
149150
RedirectStandardInput = true,
@@ -190,18 +191,25 @@ protected override IReadOnlyList<Package> _getInstalledPackages_UnSafe()
190191
return Packages;
191192
}
192193

193-
protected override ManagerStatus LoadManager()
194+
public override IReadOnlyList<string> FindCandidateExecutableFiles()
194195
{
195-
ManagerStatus status = new();
196+
List<string> candidates = [];
196197

197-
string old_choco_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs\\WingetUI\\choco-cli");
198-
string new_choco_path = Path.Join(CoreData.UniGetUIDataDirectory, "Chocolatey");
198+
if (!Settings.Get(Settings.K.UseSystemChocolatey))
199+
{
200+
candidates.Add(Path.Join(NewChocoPath, "choco.exe"));
201+
}
202+
candidates.AddRange(CoreTools.WhichMultiple("choco.exe"));
203+
return candidates;
204+
}
199205

200-
if (!Directory.Exists(old_choco_path))
206+
protected override ManagerStatus LoadManager()
207+
{
208+
if (!Directory.Exists(OldChocoPath))
201209
{
202210
Logger.Debug("Old chocolatey path does not exist, not migrating Chocolatey");
203211
}
204-
else if (CoreTools.IsSymbolicLinkDir(old_choco_path))
212+
else if (CoreTools.IsSymbolicLinkDir(OldChocoPath))
205213
{
206214
Logger.ImportantInfo("Old chocolatey path is a symbolic link, not migrating Chocolatey...");
207215
}
@@ -217,20 +225,20 @@ protected override ManagerStatus LoadManager()
217225

218226
string current_env_var =
219227
Environment.GetEnvironmentVariable("chocolateyinstall", EnvironmentVariableTarget.User) ?? "";
220-
if (current_env_var != "" && Path.GetRelativePath(current_env_var, old_choco_path) == ".")
228+
if (current_env_var != "" && Path.GetRelativePath(current_env_var, OldChocoPath) == ".")
221229
{
222230
Logger.ImportantInfo("Migrating ChocolateyInstall environment variable to new location");
223-
Environment.SetEnvironmentVariable("chocolateyinstall", new_choco_path, EnvironmentVariableTarget.User);
231+
Environment.SetEnvironmentVariable("chocolateyinstall", NewChocoPath, EnvironmentVariableTarget.User);
224232
}
225233

226-
if (!Directory.Exists(new_choco_path))
234+
if (!Directory.Exists(NewChocoPath))
227235
{
228-
Directory.CreateDirectory(new_choco_path);
236+
Directory.CreateDirectory(NewChocoPath);
229237
}
230238

231-
foreach (string old_subdir in Directory.GetDirectories(old_choco_path, "*", SearchOption.AllDirectories))
239+
foreach (string old_subdir in Directory.GetDirectories(OldChocoPath, "*", SearchOption.AllDirectories))
232240
{
233-
string new_subdir = old_subdir.Replace(old_choco_path, new_choco_path);
241+
string new_subdir = old_subdir.Replace(OldChocoPath, NewChocoPath);
234242
if (!Directory.Exists(new_subdir))
235243
{
236244
Logger.Debug("New directory: " + new_subdir);
@@ -242,9 +250,9 @@ protected override ManagerStatus LoadManager()
242250
}
243251
}
244252

245-
foreach (string old_file in Directory.GetFiles(old_choco_path, "*", SearchOption.AllDirectories))
253+
foreach (string old_file in Directory.GetFiles(OldChocoPath, "*", SearchOption.AllDirectories))
246254
{
247-
string new_file = old_file.Replace(old_choco_path, new_choco_path);
255+
string new_file = old_file.Replace(OldChocoPath, NewChocoPath);
248256
if (!File.Exists(new_file))
249257
{
250258
Logger.Info("Copying " + old_file);
@@ -257,7 +265,7 @@ protected override ManagerStatus LoadManager()
257265
}
258266
}
259267

260-
foreach (string old_subdir in Directory.GetDirectories(old_choco_path, "*", SearchOption.AllDirectories).Reverse())
268+
foreach (string old_subdir in Directory.GetDirectories(OldChocoPath, "*", SearchOption.AllDirectories).Reverse())
261269
{
262270
if (!Directory.EnumerateFiles(old_subdir).Any() && !Directory.EnumerateDirectories(old_subdir).Any())
263271
{
@@ -266,15 +274,15 @@ protected override ManagerStatus LoadManager()
266274
}
267275
}
268276

269-
if (!Directory.EnumerateFiles(old_choco_path).Any() && !Directory.EnumerateDirectories(old_choco_path).Any())
277+
if (!Directory.EnumerateFiles(OldChocoPath).Any() && !Directory.EnumerateDirectories(OldChocoPath).Any())
270278
{
271-
Logger.Info("Deleting old Chocolatey directory " + old_choco_path);
272-
Directory.Delete(old_choco_path);
279+
Logger.Info("Deleting old Chocolatey directory " + OldChocoPath);
280+
Directory.Delete(OldChocoPath);
273281
}
274282

275-
CoreTools.CreateSymbolicLinkDir(old_choco_path, new_choco_path);
283+
CoreTools.CreateSymbolicLinkDir(OldChocoPath, NewChocoPath);
276284
Settings.Set(Settings.K.ChocolateySymbolicLinkCreated, true);
277-
Logger.Info($"Symbolic link created successfully from {old_choco_path} to {new_choco_path}.");
285+
Logger.Info($"Symbolic link created successfully from {OldChocoPath} to {NewChocoPath}.");
278286

279287
}
280288
catch (Exception e)
@@ -284,21 +292,8 @@ protected override ManagerStatus LoadManager()
284292
}
285293
}
286294

287-
if (Settings.Get(Settings.K.UseSystemChocolatey))
288-
{
289-
status.ExecutablePath = CoreTools.Which("choco.exe").Item2;
290-
}
291-
else if (File.Exists(Path.Join(new_choco_path, "choco.exe")))
292-
{
293-
status.ExecutablePath = Path.Join(new_choco_path, "choco.exe");
294-
}
295-
else
296-
{
297-
status.ExecutablePath = Path.Join(CoreData.UniGetUIDataDirectory, "Chocolatey", "choco.exe");
298-
if (!File.Exists(status.ExecutablePath)) status.ExecutablePath = "";
299-
}
300-
301-
status.Found = File.Exists(status.ExecutablePath);
295+
var (found, executable) = GetExecutableFile();
296+
ManagerStatus status = new() { Found = found, ExecutablePath = executable, ExecutableCallArgs = "", };
302297

303298
if (!status.Found)
304299
{
@@ -324,7 +319,7 @@ protected override ManagerStatus LoadManager()
324319
// If the user is running bundled chocolatey and chocolatey is not in path, add chocolatey to path
325320
if (!Settings.Get(Settings.K.UseSystemChocolatey)
326321
&& !File.Exists("C:\\ProgramData\\Chocolatey\\bin\\choco.exe"))
327-
/* && Settings.Get(Settings.K.ShownWelcomeWizard)) */
322+
/* && Settings.Get(Settings.K.ShownWelcomeWizard)) */
328323
{
329324
string? path = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User);
330325
if (!path?.Contains(status.ExecutablePath.Replace("\\choco.exe", "\\bin")) ?? false)

0 commit comments

Comments
 (0)