Skip to content

Commit d9480e0

Browse files
committed
Update WinGet manager for pinget 0.4
1 parent a8f05f5 commit d9480e0

8 files changed

Lines changed: 214 additions & 33 deletions

File tree

src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
<PackageReference Include="Octokit" Version="14.0.0" />
9595
<PackageReference Include="Avalonia.Controls.WebView" Version="12.0.0" />
9696
<PackageReference Include="Tmds.DBus.Protocol" Version="0.92.0" />
97-
<PackageReference Include="Devolutions.Pinget.Cli.Rust" Version="0.4.0" GeneratePathProperty="true" ExcludeAssets="build;buildTransitive;native" />
97+
<PackageReference Include="Devolutions.Pinget.Cli.Rust" Version="0.4.2" GeneratePathProperty="true" ExcludeAssets="build;buildTransitive;native" />
9898
</ItemGroup>
9999

100100
<ItemGroup>

src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetCliHelper.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal sealed partial class PingetCliHelper : IWinGetManagerHelper
1818
private static readonly JsonSerializerOptions SerializationOptions = new()
1919
{
2020
PropertyNameCaseInsensitive = true,
21+
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
2122
};
2223
private static readonly PingetCliJsonContext SerializationContext = new(SerializationOptions);
2324

@@ -77,7 +78,7 @@ public IReadOnlyList<Package> GetInstalledPackages_UnSafe()
7778
{
7879
ListResponse result = RunJson<ListResponse>(
7980
LoggableTaskType.ListInstalledPackages,
80-
"list --accept-source-agreements --output json"
81+
"list --output json"
8182
);
8283

8384
return result
@@ -97,7 +98,7 @@ public IReadOnlyList<Package> FindPackages_UnSafe(string query)
9798
{
9899
SearchResponse result = RunJson<SearchResponse>(
99100
LoggableTaskType.FindPackages,
100-
$"search {Quote(query)} --accept-source-agreements --output json"
101+
$"search {Quote(query)} --output json"
101102
);
102103

103104
return result
@@ -115,9 +116,12 @@ public IReadOnlyList<Package> FindPackages_UnSafe(string query)
115116

116117
public IReadOnlyList<IManagerSource> GetSources_UnSafe()
117118
{
119+
// pinget 0.4.1 dropped JSON support for `source list`; `source export` is the
120+
// equivalent JSON-emitting command (PascalCase keys, but case-insensitive matching
121+
// in SerializationOptions binds them to our records).
118122
PingetSourcesResponse result = RunJson<PingetSourcesResponse>(
119123
LoggableTaskType.ListSources,
120-
"source list --output json"
124+
"source export --output json"
121125
);
122126

123127
return result
@@ -133,7 +137,7 @@ public IReadOnlyList<string> GetInstallableVersions_Unsafe(IPackage package)
133137
{
134138
VersionsResult result = RunJson<VersionsResult>(
135139
LoggableTaskType.LoadPackageVersions,
136-
$"show {WinGetPkgOperationHelper.GetIdNamePiece(package)} --versions --accept-source-agreements --output json"
140+
$"show {WinGetPkgOperationHelper.GetIdNamePiece(package)} --versions --output json"
137141
);
138142

139143
return result
@@ -203,9 +207,11 @@ private T RunJson<T>(LoggableTaskType taskType, string arguments)
203207
}
204208

205209
process.Start();
206-
string output = process.StandardOutput.ReadToEnd();
207-
string error = process.StandardError.ReadToEnd();
210+
var stdoutTask = process.StandardOutput.ReadToEndAsync();
211+
var stderrTask = process.StandardError.ReadToEndAsync();
208212
process.WaitForExit();
213+
string output = stdoutTask.GetAwaiter().GetResult();
214+
string error = stderrTask.GetAwaiter().GetResult();
209215

210216
logger.AddToStdOut(output.Split(Environment.NewLine));
211217
logger.AddToStdErr(error.Split(Environment.NewLine));

src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetPackageDetailsProvider.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ private static IReadOnlyList<string> BuildShowArguments(PackageQuery query, stri
142142
arguments.Add(query.Version);
143143
}
144144

145-
arguments.Add("--accept-source-agreements");
146145
arguments.Add("--output");
147146
arguments.Add("json");
148147
return arguments;

src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ protected override IReadOnlyList<string> _getOperationParameters(
3535
OperationType operation
3636
)
3737
{
38+
// Pinget 0.4.x does not accept --accept-source-agreements, --disable-interactivity,
39+
// or --proxy on any verb; --accept-package-agreements, --force, --location, and
40+
// --interactive are accepted on install/uninstall but rejected on upgrade.
41+
bool usePinget =
42+
((WinGet)Manager).SelectedCliToolKind == WinGetCliToolKind.BundledPinget;
43+
3844
List<string> parameters =
3945
[
4046
operation switch
@@ -51,7 +57,10 @@ OperationType operation
5157
{
5258
parameters.AddRange(["--source", package.Source.Name]);
5359
}
54-
parameters.AddRange(["--accept-source-agreements", "--disable-interactivity"]);
60+
if (!usePinget)
61+
{
62+
parameters.AddRange(["--accept-source-agreements", "--disable-interactivity"]);
63+
}
5564

5665
// package.OverridenInstallationOptions.Scope is meaningless in WinGet packages. Default is unspecified, hence the _ => [].
5766
parameters.AddRange(
@@ -76,7 +85,15 @@ operation is OperationType.Uninstall
7685
parameters.AddRange(["--version", $"\"{options.Version}\""]);
7786
}
7887

79-
parameters.Add(options.InteractiveInstallation ? "--interactive" : "--silent");
88+
if (usePinget && operation is OperationType.Update)
89+
{
90+
// pinget upgrade only supports --silent (no --interactive).
91+
parameters.Add("--silent");
92+
}
93+
else
94+
{
95+
parameters.Add(options.InteractiveInstallation ? "--interactive" : "--silent");
96+
}
8097

8198
if (operation is OperationType.Update)
8299
{
@@ -90,16 +107,19 @@ operation is OperationType.Uninstall
90107
}
91108
parameters.Add("--include-unknown");
92109

93-
if (options.CustomInstallLocation != "")
110+
if (!usePinget)
94111
{
95-
if (Settings.Get(Settings.K.WinGetForceLocationOnUpdate))
96-
parameters.AddRange(["--location", $"\"{options.CustomInstallLocation}\""]);
97-
}
98-
else
99-
{
100-
var detectedLocation = TryGetPortableInstallLocation(package);
101-
if (detectedLocation is not null)
102-
parameters.AddRange(["--location", $"\"{detectedLocation}\""]);
112+
if (options.CustomInstallLocation != "")
113+
{
114+
if (Settings.Get(Settings.K.WinGetForceLocationOnUpdate))
115+
parameters.AddRange(["--location", $"\"{options.CustomInstallLocation}\""]);
116+
}
117+
else
118+
{
119+
var detectedLocation = TryGetPortableInstallLocation(package);
120+
if (detectedLocation is not null)
121+
parameters.AddRange(["--location", $"\"{detectedLocation}\""]);
122+
}
103123
}
104124
}
105125
else if (operation is OperationType.Install)
@@ -110,7 +130,11 @@ operation is OperationType.Uninstall
110130

111131
if (operation is not OperationType.Uninstall)
112132
{
113-
parameters.AddRange(["--accept-package-agreements", "--force"]);
133+
// pinget upgrade does not accept --accept-package-agreements or --force.
134+
if (!(usePinget && operation is OperationType.Update))
135+
{
136+
parameters.AddRange(["--accept-package-agreements", "--force"]);
137+
}
114138

115139
if (options.SkipHashCheck)
116140
parameters.Add("--ignore-security-hash");
@@ -189,7 +213,10 @@ or ElevationRequirement.ElevatesSelf
189213
Logger.Error(ex);
190214
}
191215

192-
parameters.Add(WinGet.GetProxyArgument());
216+
if (!usePinget)
217+
{
218+
parameters.Add(WinGet.GetProxyArgument());
219+
}
193220

194221
parameters.AddRange(
195222
operation switch

src/UniGetUI.PackageEngine.Managers.WinGet/UniGetUI.PackageEngine.Managers.WinGet.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
</ItemGroup>
2525

2626
<ItemGroup>
27-
<PackageReference Include="Devolutions.Pinget.Core" Version="0.3.0" />
27+
<PackageReference Include="Devolutions.Pinget.Core" Version="0.4.2" />
2828
<PackageReference Include="PhotoSauce.MagicScaler" Version="0.15.0" />
2929
</ItemGroup>
3030
</Project>

src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,10 @@ public override void RefreshPackageIndexes()
725725
FileName = Status.ExecutablePath,
726726
Arguments =
727727
Status.ExecutableCallArgs
728-
+ " source update --disable-interactivity "
728+
+ " source update"
729+
+ (SelectedCliToolKind == WinGetCliToolKind.SystemWinGet
730+
? " --disable-interactivity "
731+
: " ")
729732
+ GetCliToolProxyArgument(),
730733
UseShellExecute = false,
731734
RedirectStandardOutput = true,

src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs

Lines changed: 155 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
using UniGetUI.Core.Data;
44
using UniGetUI.Core.SettingsEngine;
55
using UniGetUI.PackageEngine.Classes.Manager;
6+
using UniGetUI.PackageEngine.Enums;
67
using UniGetUI.PackageEngine.Interfaces;
78
using UniGetUI.PackageEngine.Managers.WingetManager;
89
using UniGetUI.PackageEngine.ManagerClasses.Classes;
910
using UniGetUI.PackageEngine.PackageClasses;
11+
using UniGetUI.PackageEngine.Serializable;
1012
using UniGetUI.PackageEngine.Tests.Infrastructure.Assertions;
1113
using UniGetUI.PackageEngine.Tests.Infrastructure.Builders;
1214

@@ -298,23 +300,24 @@ public void FindCandidateExecutableFilesReturnsEmptyWhenNoCliToolExists()
298300
[Fact]
299301
public void PingetCliHelperDeserializesListResponsesWithGeneratedContext()
300302
{
303+
// pinget 0.4.1+ emits snake_case keys.
301304
const string json = """
302305
{
303306
"matches": [
304307
{
305308
"name": "Contoso Tool",
306309
"id": "Contoso.Tool",
307-
"localId": null,
308-
"installedVersion": "1.2.3",
309-
"availableVersion": "2.0.0",
310-
"sourceName": "winget",
310+
"local_id": null,
311+
"installed_version": "1.2.3",
312+
"available_version": "2.0.0",
313+
"source_name": "winget",
311314
"publisher": null,
312315
"scope": null,
313-
"installerCategory": null,
314-
"installLocation": null,
315-
"packageFamilyNames": [],
316-
"productCodes": [],
317-
"upgradeCodes": []
316+
"installer_category": null,
317+
"install_location": null,
318+
"package_family_names": [],
319+
"product_codes": [],
320+
"upgrade_codes": []
318321
}
319322
],
320323
"warnings": [],
@@ -708,6 +711,149 @@ public void WinGetCliHelperUsesPingetProviderForPackageDetails()
708711
);
709712
}
710713

714+
[Theory]
715+
[InlineData(0)] // OperationType.Install
716+
[InlineData(1)] // OperationType.Update
717+
[InlineData(2)] // OperationType.Uninstall
718+
public void WinGetOperationHelperEmitsWinGetCompatibleFlagsForSystemCli(int operationType)
719+
{
720+
var manager = new WinGet();
721+
SetCliToolKind(manager, WinGetCliToolKind.SystemWinGet);
722+
var package = new PackageBuilder()
723+
.WithManager(manager)
724+
.WithId("Contoso.Tool")
725+
.WithVersion("1.0.0")
726+
.WithNewVersion("2.0.0")
727+
.Build();
728+
729+
var parameters = manager.OperationHelper.GetParameters(
730+
package,
731+
new InstallOptions(),
732+
(OperationType)operationType
733+
);
734+
735+
Assert.Contains("--accept-source-agreements", parameters);
736+
Assert.Contains("--disable-interactivity", parameters);
737+
}
738+
739+
[Fact]
740+
public void WinGetOperationHelperSkipsUnsupportedFlagsForPingetInstall()
741+
{
742+
var manager = new WinGet();
743+
SetCliToolKind(manager, WinGetCliToolKind.BundledPinget);
744+
var package = new PackageBuilder()
745+
.WithManager(manager)
746+
.WithId("Spotify.Spotify")
747+
.Build();
748+
749+
var parameters = manager.OperationHelper.GetParameters(
750+
package,
751+
new InstallOptions(),
752+
OperationType.Install
753+
);
754+
755+
Assert.DoesNotContain("--accept-source-agreements", parameters);
756+
Assert.DoesNotContain("--disable-interactivity", parameters);
757+
// pinget install does accept these.
758+
Assert.Contains("--accept-package-agreements", parameters);
759+
Assert.Contains("--force", parameters);
760+
Assert.Contains("--silent", parameters);
761+
}
762+
763+
[Fact]
764+
public void WinGetOperationHelperSkipsUnsupportedFlagsForPingetUpgrade()
765+
{
766+
var manager = new WinGet();
767+
SetCliToolKind(manager, WinGetCliToolKind.BundledPinget);
768+
var package = new PackageBuilder()
769+
.WithManager(manager)
770+
.WithId("Contoso.Tool")
771+
.WithVersion("1.0.0")
772+
.WithNewVersion("2.0.0")
773+
.Build();
774+
var options = new InstallOptions
775+
{
776+
InteractiveInstallation = true,
777+
CustomInstallLocation = @"C:\Apps\Contoso",
778+
};
779+
780+
var parameters = manager.OperationHelper.GetParameters(
781+
package,
782+
options,
783+
OperationType.Update
784+
);
785+
786+
Assert.DoesNotContain("--accept-source-agreements", parameters);
787+
Assert.DoesNotContain("--disable-interactivity", parameters);
788+
// pinget upgrade does NOT accept these even though winget upgrade does.
789+
Assert.DoesNotContain("--accept-package-agreements", parameters);
790+
Assert.DoesNotContain("--force", parameters);
791+
Assert.DoesNotContain("--interactive", parameters);
792+
Assert.DoesNotContain("--location", parameters);
793+
// pinget upgrade still supports --include-unknown and --silent.
794+
Assert.Contains("--include-unknown", parameters);
795+
Assert.Contains("--silent", parameters);
796+
}
797+
798+
[Fact]
799+
public void WinGetOperationHelperSkipsUnsupportedFlagsForPingetUninstall()
800+
{
801+
var manager = new WinGet();
802+
SetCliToolKind(manager, WinGetCliToolKind.BundledPinget);
803+
var package = new PackageBuilder()
804+
.WithManager(manager)
805+
.WithId("Contoso.Tool")
806+
.WithVersion("1.0.0")
807+
.Build();
808+
809+
var parameters = manager.OperationHelper.GetParameters(
810+
package,
811+
new InstallOptions(),
812+
OperationType.Uninstall
813+
);
814+
815+
Assert.DoesNotContain("--accept-source-agreements", parameters);
816+
Assert.DoesNotContain("--disable-interactivity", parameters);
817+
}
818+
819+
[Fact]
820+
public void WinGetOperationHelperOmitsProxyArgumentForPinget()
821+
{
822+
Settings.Set(Settings.K.EnableProxy, true);
823+
Settings.Set(Settings.K.EnableProxyAuth, false);
824+
Settings.SetValue(Settings.K.ProxyURL, "http://proxy.example.test:3128/");
825+
try
826+
{
827+
var manager = new WinGet();
828+
SetCliToolKind(manager, WinGetCliToolKind.BundledPinget);
829+
var package = new PackageBuilder()
830+
.WithManager(manager)
831+
.WithId("Contoso.Tool")
832+
.Build();
833+
834+
var parameters = manager.OperationHelper.GetParameters(
835+
package,
836+
new InstallOptions(),
837+
OperationType.Install
838+
);
839+
840+
Assert.DoesNotContain(parameters, p => p.StartsWith("--proxy", StringComparison.Ordinal));
841+
}
842+
finally
843+
{
844+
Settings.Set(Settings.K.EnableProxy, false);
845+
Settings.SetValue(Settings.K.ProxyURL, "");
846+
}
847+
}
848+
849+
private static void SetCliToolKind(WinGet manager, WinGetCliToolKind kind)
850+
{
851+
typeof(WinGet)
852+
.GetProperty(nameof(WinGet.SelectedCliToolKind), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!
853+
.GetSetMethod(nonPublic: true)!
854+
.Invoke(manager, [kind]);
855+
}
856+
711857
private sealed class TestableWinGet : WinGet
712858
{
713859
public IReadOnlyList<Package> InvokeGetInstalledPackages() => base.GetInstalledPackages_UnSafe();

0 commit comments

Comments
 (0)