Skip to content

Commit f11fb78

Browse files
authored
Stop claiming a machine scope and a package index Pip never resolved (#5466)
1 parent 30ea545 commit f11fb78

2 files changed

Lines changed: 188 additions & 4 deletions

File tree

‎src/UniGetUI.PackageEngine.AgentBroker/BrokerRequestBuilder.cs‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ manager is ManagerName.PowerShell
8383
Source = new RequestSource
8484
{
8585
Name = package.Source.Name,
86-
Url = package.Source.Url?.ToString(),
86+
Url = SourceUrlIdentifiesAnIndex(manager)
87+
? package.Source.Url?.ToString()
88+
: null,
8789
},
8890
Package = new RequestPackage
8991
{
@@ -97,7 +99,7 @@ manager is ManagerName.PowerShell
9799
// matching the local WinGet execution path.
98100
Scope = dropArchAndScope
99101
? null
100-
: MapScope(package.OverridenOptions.Scope ?? options.InstallationScope),
102+
: MapScope(manager, package.OverridenOptions.Scope ?? options.InstallationScope),
101103
Interactive = options.InteractiveInstallation,
102104
SkipHashCheck = options.SkipHashCheck,
103105
PreRelease = options.PreRelease,
@@ -175,22 +177,32 @@ private static bool TryMapManagerName(string managerName, out ManagerName mapped
175177
return result is not null;
176178
}
177179

178-
private static Scope? MapScope(string? scope)
180+
private static Scope? MapScope(ManagerName manager, string? scope)
179181
{
180182
if (string.IsNullOrEmpty(scope))
181183
{
182184
return null;
183185
}
184186

185-
return scope.ToLowerInvariant() switch
187+
Scope? mapped = scope.ToLowerInvariant() switch
186188
{
187189
"user" => Scope.User,
188190
"machine" => Scope.Machine,
189191
"global" => Scope.Machine,
190192
_ => null,
191193
};
194+
195+
return mapped is Scope.Machine && !ManagerScopeDistinguishesSystemWideInstalls(manager)
196+
? null
197+
: mapped;
192198
}
193199

200+
private static bool ManagerScopeDistinguishesSystemWideInstalls(ManagerName manager) =>
201+
manager is not ManagerName.Pip;
202+
203+
private static bool SourceUrlIdentifiesAnIndex(ManagerName manager) =>
204+
manager is not ManagerName.Pip;
205+
194206
private static BrokerArchitecture? MapArchitecture(string? architecture)
195207
{
196208
if (string.IsNullOrEmpty(architecture))

‎src/UniGetUI.PackageEngine.Tests/BrokerRequestBuilderTests.cs‎

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Devolutions.Now.Policy.Api;
2+
using Devolutions.Now.Policy.Client;
23
using UniGetUI.PackageEngine.AgentBroker;
34
using UniGetUI.PackageEngine.Serializable;
45
using UniGetUI.PackageEngine.Tests.Infrastructure.Builders;
@@ -16,6 +17,12 @@ private static UniGetUI.PackageEngine.PackageClasses.Package BuildWinGetPackage(
1617
.WithId("Contoso.Test")
1718
.Build();
1819

20+
private static UniGetUI.PackageEngine.PackageClasses.Package BuildPipPackage()
21+
=> new PackageBuilder()
22+
.WithManager(new PackageManagerBuilder().WithName("Pip").Build())
23+
.WithId("requests")
24+
.Build();
25+
1926
private static UniGetUI.PackageEngine.PackageClasses.Package BuildPowerShellPackage()
2027
=> new PackageBuilder()
2128
.WithManager(new PackageManagerBuilder().WithName("PowerShell").Build())
@@ -100,6 +107,71 @@ public void Build_PackageScopeOverride_TakesPrecedenceOverSavedScope()
100107
Assert.Equal(Scope.User, request.Options.Scope);
101108
}
102109

110+
[Fact]
111+
public void Build_OmitsMachineScopeForPip()
112+
{
113+
var package = BuildPipPackage();
114+
package.OverridenOptions.Scope = PackageScope.Global;
115+
var options = new InstallOptions { InstallationScope = PackageScope.Machine };
116+
117+
var request = BrokerRequestBuilder.Build(package, options, OperationType.Update);
118+
119+
Assert.Null(request.Options.Scope);
120+
}
121+
122+
[Fact]
123+
public void Build_KeepsUserScopeForPip()
124+
{
125+
var package = BuildPipPackage();
126+
package.OverridenOptions.Scope = PackageScope.User;
127+
128+
var request = BrokerRequestBuilder.Build(package, new InstallOptions(), OperationType.Update);
129+
130+
Assert.Equal(Scope.User, request.Options.Scope);
131+
}
132+
133+
[Fact]
134+
public void Build_OmitsAConfiguredMachineScopeForPip()
135+
{
136+
var package = BuildPipPackage();
137+
var options = new InstallOptions { InstallationScope = PackageScope.Machine };
138+
139+
var request = BrokerRequestBuilder.Build(package, options, OperationType.Install);
140+
141+
Assert.Null(request.Options.Scope);
142+
}
143+
144+
[Fact]
145+
public void Build_KeepsAConfiguredMachineScopeForManagersThatResolveIt()
146+
{
147+
var package = BuildWinGetPackage();
148+
var options = new InstallOptions { InstallationScope = PackageScope.Machine };
149+
150+
var request = BrokerRequestBuilder.Build(package, options, OperationType.Install);
151+
152+
Assert.Equal(Scope.Machine, request.Options.Scope);
153+
}
154+
155+
[Fact]
156+
public void Build_OmitsTheSourceUrlForPipButKeepsTheSourceName()
157+
{
158+
var package = BuildPipPackage();
159+
160+
var request = BrokerRequestBuilder.Build(package, new InstallOptions(), OperationType.Update);
161+
162+
Assert.Null(request.Source.Url);
163+
Assert.Equal(package.Source.Name, request.Source.Name);
164+
}
165+
166+
[Fact]
167+
public void Build_KeepsTheSourceUrlForOtherManagers()
168+
{
169+
var package = BuildWinGetPackage();
170+
171+
var request = BrokerRequestBuilder.Build(package, new InstallOptions(), OperationType.Update);
172+
173+
Assert.Equal(package.Source.Url?.ToString(), request.Source.Url);
174+
}
103175
[Fact]
104176
public void Build_DropArchAndScopeRetry_OmitsScopeAndArchitecture()
105177
{
@@ -310,4 +382,104 @@ public void Build_KeepsWinGetVersionsThatAreNotPlainVersions()
310382
Assert.Equal("2021 Update", request.Package.Version);
311383
}
312384

385+
[Fact]
386+
public async Task Build_PipUpdateRequest_ClearsTheBrokerCapabilityCheck()
387+
{
388+
var package = BuildPipPackage();
389+
package.OverridenOptions.Scope = PackageScope.Global;
390+
391+
var request = BrokerRequestBuilder.Build(package, new InstallOptions(), OperationType.Update);
392+
393+
using var client = CreatePipBrokerClient();
394+
395+
var execution = await client.Execute(request, CancellationToken.None);
396+
397+
Assert.Equal(Decision.Allow, execution.Decision.Decision);
398+
}
399+
400+
[Fact]
401+
public async Task Build_PipRequestWithAConfiguredMachineScope_ClearsTheBrokerCapabilityCheck()
402+
{
403+
var package = BuildPipPackage();
404+
var options = new InstallOptions { InstallationScope = PackageScope.Machine };
405+
406+
var request = BrokerRequestBuilder.Build(package, options, OperationType.Update);
407+
408+
using var client = CreatePipBrokerClient();
409+
410+
var execution = await client.Execute(request, CancellationToken.None);
411+
412+
Assert.Equal(Decision.Allow, execution.Decision.Decision);
413+
}
414+
415+
private const string TestEffectiveUser = "TESTDOMAIN\\tester";
416+
private const string TestClientExecutablePath = "C:\\test\\unigetui.exe";
417+
418+
private static BrokerClient CreatePipBrokerClient()
419+
=> new(new BrokerClientOptions
420+
{
421+
Transport = new PipCapabilityTransport(),
422+
RequestedElevation = Elevation.Standard,
423+
EffectiveUser = TestEffectiveUser,
424+
ClientExecutablePath = TestClientExecutablePath,
425+
ClientVersion = "0.0.0-tests",
426+
});
427+
428+
private sealed class PipCapabilityTransport : IBrokerTransport
429+
{
430+
public Transport Kind => Transport.HttpNamedPipe;
431+
432+
public Task<BrokerTransportResponse> Send(
433+
BrokerTransportRequest request,
434+
CancellationToken cancellationToken = default
435+
) => request.Path switch
436+
{
437+
"/v1/capabilities" => Json(BrokerSerializer.Serialize(Capabilities())),
438+
"/v1/package-operations/execute" => Json(BrokerSerializer.Serialize(Execution())),
439+
_ => throw new BrokerClientException(
440+
BrokerClientErrorKind.InvalidRequest,
441+
$"Unexpected request path: {request.Path}",
442+
request.Path
443+
),
444+
};
445+
446+
public void Dispose() { }
447+
448+
private static Task<BrokerTransportResponse> Json(string body) =>
449+
Task.FromResult(new BrokerTransportResponse { StatusCode = 200, Body = body });
450+
451+
private static CapabilitiesResponse Capabilities() => new()
452+
{
453+
ResponseKind = BrokerApi.CapabilitiesResponseKind,
454+
ResponseVersion = BrokerApi.Version,
455+
MaxRequestBodyBytes = 1_000_000,
456+
Transports = [Transport.HttpNamedPipe],
457+
Managers =
458+
[
459+
new ManagerCapability
460+
{
461+
Manager = ManagerName.Pip,
462+
Operations = [Operation.Install, Operation.Update, Operation.Uninstall],
463+
Scopes = [Scope.User],
464+
Architectures = [Architecture.Neutral],
465+
SupportsCustomParameters = false,
466+
SupportsCustomInstallLocation = false,
467+
SupportsCaptureOutput = true,
468+
},
469+
],
470+
};
471+
472+
private static ExecutionResponse Execution() => new()
473+
{
474+
ResponseKind = BrokerApi.ExecutionResponseKind,
475+
ResponseVersion = BrokerApi.Version,
476+
Decision = new DecisionInfo { Decision = Decision.Allow },
477+
Operation = new OperationSubmission
478+
{
479+
OperationId = "test-pip-operation",
480+
Status = OperationStatus.Starting,
481+
SubmittedAt = DateTimeOffset.UtcNow,
482+
},
483+
};
484+
}
313485
}

0 commit comments

Comments
 (0)