Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,23 @@ public class Chocolatey : BaseNuGet
"no",
];
private const string DefaultSystemChocoPath = @"C:\ProgramData\chocolatey\bin\choco.exe";
private const string LegacyInstallVariable = "ChocolateyInstall";
private static int _inheritedProcessValueSanitized;
private static readonly string[] LegacyBundledChocolateyPaths =
[
Path.Join(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Programs\\WingetUI\\choco-cli"
),
Path.Join(CoreData.UniGetUIDataDirectory, "Chocolatey"),
Path.Join(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"UniGetUI\\Chocolatey"
),
Path.Join(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
".wingetui\\Chocolatey"
),
];

// AttemptFastRepair is a no-op here, so retrying a timed-out choco listing just spawns another (#4974).
Expand Down Expand Up @@ -171,6 +181,179 @@ public static bool HasLegacyBundledInstallation()
return false;
}

protected override void _performPreInitializationSteps()
{
RemoveStaleLegacyInstallVariable();
}

public static bool IsLegacyBundledChocolateyRoot(string? path)
{
string? normalized = NormalizeDirectory(path);
if (normalized is null)
{
return false;
}

foreach (string legacyPath in LegacyBundledChocolateyPaths)
{
string? legacyRoot = NormalizeDirectory(legacyPath);
if (legacyRoot is null)
{
continue;
}

if (normalized.Equals(legacyRoot, StringComparison.OrdinalIgnoreCase))
{
return true;
}

if (
normalized.StartsWith(
legacyRoot + Path.DirectorySeparatorChar,
StringComparison.OrdinalIgnoreCase
)
)
{
return true;
}
}

return false;
}

private static string? NormalizeDirectory(string? path)
{
if (string.IsNullOrWhiteSpace(path))
{
return null;
}

try
{
return Path.TrimEndingDirectorySeparator(
Path.GetFullPath(
Environment.ExpandEnvironmentVariables(path.Trim().Trim('"'))
)
);
}
catch (Exception)
{
return null;
}
}

internal readonly record struct LegacyInstallVariablePlan(
bool RemoveUserValue,
bool ReplaceProcessValue,
string? NewProcessValue
);

internal static LegacyInstallVariablePlan PlanStaleLegacyInstallVariableRemoval(
string? userValue,
string? machineValue,
string? processValue
)
{
bool removeUserValue = IsLegacyBundledChocolateyRoot(userValue);
string? remainingUserValue = removeUserValue ? null : userValue;

if (!IsLegacyBundledChocolateyRoot(processValue))
{
return new LegacyInstallVariablePlan(removeUserValue, false, processValue);
}

string? replacement = null;
if (!string.IsNullOrWhiteSpace(remainingUserValue))
{
replacement = remainingUserValue;
}
else if (!string.IsNullOrWhiteSpace(machineValue))
{
replacement = machineValue;
}

if (IsLegacyBundledChocolateyRoot(replacement))
{
replacement = null;
}

return new LegacyInstallVariablePlan(removeUserValue, true, replacement);
}

internal static void TEST_ResetInheritedProcessValueSanitation()
{
Interlocked.Exchange(ref _inheritedProcessValueSanitized, 0);
}

internal static void RemoveStaleLegacyInstallVariable()
Comment thread
GabrielDuf marked this conversation as resolved.
{
if (!OperatingSystem.IsWindows())
{
return;
}

try
{
bool sanitizeInheritedProcessValue =
Interlocked.Exchange(ref _inheritedProcessValueSanitized, 1) == 0;

string? userValue = Environment.GetEnvironmentVariable(
LegacyInstallVariable,
EnvironmentVariableTarget.User
);
string? machineValue = Environment.GetEnvironmentVariable(
LegacyInstallVariable,
EnvironmentVariableTarget.Machine
);
string? processValue = Environment.GetEnvironmentVariable(
LegacyInstallVariable,
EnvironmentVariableTarget.Process
);

LegacyInstallVariablePlan plan = PlanStaleLegacyInstallVariableRemoval(
userValue,
machineValue,
processValue
);

if (plan.RemoveUserValue)
{
Logger.ImportantInfo(
$"Removing the stale {LegacyInstallVariable} user environment variable, which "
+ $"pointed at the no longer supported bundled Chocolatey path {userValue}"
);

Environment.SetEnvironmentVariable(
LegacyInstallVariable,
null,
EnvironmentVariableTarget.User
);
}

if (sanitizeInheritedProcessValue && plan.ReplaceProcessValue)
{
Logger.ImportantInfo(
$"Refreshing the inherited {LegacyInstallVariable} process environment "
+ $"variable, which pointed at the no longer supported bundled Chocolatey "
+ $"path {processValue}, to {plan.NewProcessValue ?? "no value"}"
);

Environment.SetEnvironmentVariable(
LegacyInstallVariable,
plan.NewProcessValue,
EnvironmentVariableTarget.Process
);
}
}
catch (Exception ex)
{
Logger.Error(
$"Could not remove the stale {LegacyInstallVariable} user environment variable"
);
Logger.Error(ex);
}
}

internal IReadOnlyList<Package> ParseAvailableUpdates(IEnumerable<string> lines)
{
List<Package> packages = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ out string callArguments
);
protected abstract void _loadManagerVersion(out string version);

protected virtual void _performPreInitializationSteps() { }

protected virtual void _performExtraLoadingSteps() { }

public virtual void Initialize()
Expand All @@ -74,6 +76,7 @@ public virtual void Initialize()
{
_ready = false;
_ensurePropertlyConstructed();
_performPreInitializationSteps();

if (!IsEnabled())
{ // Do NOT initialise disabled package managers
Expand Down
Loading
Loading