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
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,12 @@ private void ApplyProfileEnableState()
SkipHashEnabled = op is not OperationType.Uninstall && caps.CanSkipIntegrityChecks;
ArchEnabled = op is not OperationType.Uninstall && caps.SupportsCustomArchitectures;
VersionEnabled = op is OperationType.Install && (caps.SupportsCustomVersions || caps.SupportsPreRelease);
ScopeEnabled = caps.SupportsCustomScopes && op switch
{
OperationType.Update => caps.SupportsCustomScopesOnUpdate,
OperationType.Uninstall => caps.SupportsCustomScopesOnUninstall,
_ => true,
};
}

// ── Live command preview ──────────────────────────────────────────────────
Expand Down
4 changes: 4 additions & 0 deletions src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public struct ManagerCapabilities
public bool SupportsCustomArchitectures = false;
public string[] SupportedCustomArchitectures = [];
public bool SupportsCustomScopes = false;
// Whether a custom scope can be applied to update/uninstall too, not just install
// (e.g. Windows PowerShell 5.x's Update-Module has no -Scope parameter)
public bool SupportsCustomScopesOnUpdate = true;
public bool SupportsCustomScopesOnUninstall = true;
public bool SupportsPreRelease = false;
public bool SupportsCustomLocations = false;
public bool SupportsCustomSources = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,11 @@ OperationType operation
// Update-Module (PowerShellGet) has no -Scope parameter; only Install-Module accepts it
if (operation is OperationType.Install && !package.OverridenOptions.PowerShell_DoNotSetScopeParameter)
{
if (
package.OverridenOptions.Scope == PackageScope.Global
|| (
package.OverridenOptions.Scope is null
&& options.InstallationScope == PackageScope.Global
)
)
parameters.AddRange(["-Scope", "AllUsers"]);
else
parameters.AddRange(["-Scope", "CurrentUser"]);
// The scope chosen in the options dialog wins; fall back to the auto-detected install scope
string scope = options.InstallationScope.Length > 0
? options.InstallationScope
: package.OverridenOptions.Scope ?? "";
parameters.AddRange(["-Scope", scope == PackageScope.Global ? "AllUsers" : "CurrentUser"]);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/UniGetUI.PackageEngine.Managers.PowerShell/PowerShell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public PowerShell()
SupportsCustomVersions = true,
CanDownloadInstaller = true,
SupportsCustomScopes = true,
// Update-Module/Uninstall-Module (PowerShellGet) take no -Scope; only Install-Module does
SupportsCustomScopesOnUpdate = false,
SupportsCustomScopesOnUninstall = false,
CanListDependencies = true,
SupportsCustomSources = true,
SupportsPreRelease = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,11 @@ OperationType operation
if (options.PreRelease)
parameters.Add("-Prerelease");

if (
package.OverridenOptions.Scope is PackageScope.Global
|| (
package.OverridenOptions.Scope is null
&& options.InstallationScope is PackageScope.Global
)
)
parameters.AddRange(["-Scope", "AllUsers"]);
else
parameters.AddRange(["-Scope", "CurrentUser"]);
// The scope chosen in the options dialog wins; fall back to the auto-detected install scope
string scope = options.InstallationScope.Length > 0
? options.InstallationScope
: package.OverridenOptions.Scope ?? "";
parameters.AddRange(["-Scope", scope == PackageScope.Global ? "AllUsers" : "CurrentUser"]);
}

parameters.AddRange(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public PowerShell7()
CanRunAsAdmin = true,
SupportsCustomVersions = true,
SupportsCustomScopes = true,
// Uninstall-PSResource is invoked without a scope, so the selector is a no-op there
SupportsCustomScopesOnUninstall = false,
SupportsCustomSources = true,
SupportsPreRelease = true,
CanDownloadInstaller = true,
Expand Down
48 changes: 48 additions & 0 deletions src/UniGetUI.PackageEngine.Tests/PowerShell7ManagerTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#if WINDOWS
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Managers.PowerShell7Manager;
using UniGetUI.PackageEngine.Serializable;

namespace UniGetUI.PackageEngine.Tests;

Expand Down Expand Up @@ -62,6 +63,53 @@ public void ParseInstalledPackages_SkipsBlankAndMalformedLines()
Assert.Equal("Pester", package.Id);
}

// Regression for https://github.com/Devolutions/UniGetUI/issues/5110:
// a scope explicitly chosen in the options dialog must override the auto-detected
// install scope, otherwise the command never changes for an installed module.
[Fact]
public void GetParameters_ExplicitScopeOverridesDetectedScope()
{
var manager = new PowerShell7();
var package = Assert.Single(PowerShell7.ParseInstalledPackages(
["##SCOPE:AllUsers##", "Devolutions.PowerShell\t2025.1.0\tPSGallery"], manager));
Assert.Equal(PackageScope.Machine, package.OverridenOptions.Scope);

var options = new InstallOptions { InstallationScope = PackageScope.User };
var parameters = manager.OperationHelper.GetParameters(package, options, OperationType.Update);

Assert.Contains("CurrentUser", parameters);
Assert.DoesNotContain("AllUsers", parameters);
}

[Fact]
public void GetParameters_ExplicitGlobalOverridesDetectedUserScope()
{
var manager = new PowerShell7();
var package = Assert.Single(PowerShell7.ParseInstalledPackages(
["##SCOPE:CurrentUser##", "Devolutions.PowerShell\t2025.1.0\tPSGallery"], manager));
Assert.Equal(PackageScope.User, package.OverridenOptions.Scope);

var options = new InstallOptions { InstallationScope = PackageScope.Machine };
var parameters = manager.OperationHelper.GetParameters(package, options, OperationType.Update);

Assert.Contains("AllUsers", parameters);
Assert.DoesNotContain("CurrentUser", parameters);
}

[Fact]
public void GetParameters_DefaultScopeFallsBackToDetectedScope()
{
var manager = new PowerShell7();
var package = Assert.Single(PowerShell7.ParseInstalledPackages(
["##SCOPE:AllUsers##", "Devolutions.PowerShell\t2025.1.0\tPSGallery"], manager));

var options = new InstallOptions();
var parameters = manager.OperationHelper.GetParameters(package, options, OperationType.Update);

Assert.Contains("AllUsers", parameters);
Assert.DoesNotContain("CurrentUser", parameters);
}

// Regression for https://github.com/Devolutions/UniGetUI/issues/4781:
// the previous Format-Table-based pipeline truncated long names (e.g.
// "Microsoft.Graph.Beta.DeviceManagement.Administration" → "Microsoft.Graph.Beta..")
Expand Down
48 changes: 48 additions & 0 deletions src/UniGetUI.PackageEngine.Tests/PowerShellManagerTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#if WINDOWS
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Managers.PowerShellManager;
using UniGetUI.PackageEngine.Serializable;

namespace UniGetUI.PackageEngine.Tests;

Expand Down Expand Up @@ -55,5 +57,51 @@ public void ParseInstalledPackages_SkipsMalformedLines()

Assert.Equal("Pester", package.Id);
}

private static UniGetUI.PackageEngine.Interfaces.IPackage BuildInstalledPackage(PowerShell manager)
=> Assert.Single(PowerShell.ParseInstalledPackages(
[
"Version Name Repository Description",
"------- ---- ---------- -----------",
"1.0.0 Devolutions.PowerShell PSGallery x",
],
manager));

[Fact]
public void GetParameters_InstallRespectsExplicitScope()
{
var manager = new PowerShell();
var package = BuildInstalledPackage(manager);

var options = new InstallOptions { InstallationScope = PackageScope.Machine };
var parameters = manager.OperationHelper.GetParameters(package, options, OperationType.Install);

Assert.Contains("-Scope", parameters);
Assert.Contains("AllUsers", parameters);
}

// Regression for https://github.com/Devolutions/UniGetUI/issues/5110:
// Update-Module (Windows PowerShell 5.x / PowerShellGet 1.0.0.1) has no -Scope parameter,
// so no scope must be emitted for an update regardless of the selected scope.
[Fact]
public void GetParameters_UpdateOmitsScope()
{
var manager = new PowerShell();
var package = BuildInstalledPackage(manager);

var options = new InstallOptions { InstallationScope = PackageScope.Machine };
var parameters = manager.OperationHelper.GetParameters(package, options, OperationType.Update);

Assert.DoesNotContain("-Scope", parameters);
}

[Fact]
public void Capabilities_ScopeAppliesToInstallOnly()
{
var manager = new PowerShell();
Assert.True(manager.Capabilities.SupportsCustomScopes);
Assert.False(manager.Capabilities.SupportsCustomScopesOnUpdate);
Assert.False(manager.Capabilities.SupportsCustomScopesOnUninstall);
}
}
#endif
Loading