Skip to content

Commit fe5a6f9

Browse files
authored
Fix MSIX installer manifests and mark unverified versions (#5369)
1 parent 1f4bd2d commit fe5a6f9

16 files changed

Lines changed: 367 additions & 25 deletions

File tree

src/Languages/lang_en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,9 @@
922922
"Select executable": "Select executable",
923923
"All files": "All files",
924924
"Download full package manifest alongside the installer": "Download full package manifest alongside the installer",
925+
"{0} could not read the version of this package that is currently installed.": "{0} could not read the version of this package that is currently installed.",
926+
"No installed version is known for it.": "No installed version is known for it.",
927+
"The version shown is the one UniGetUI last installed ({0}); if the package has been updated by anything else since, that is out of date.": "The version shown is the one UniGetUI last installed ({0}); if the package has been updated by anything else since, that is out of date.",
925928
"Prefer latest versions (may include breaking changes) instead of recommended safe updates": "Prefer latest versions (may include breaking changes) instead of recommended safe updates",
926929
"Expand": "Expand",
927930
"Fast JavaScript runtime, bundler, and package manager": "Fast JavaScript runtime, bundler, and package manager",

src/UniGetUI.Avalonia/Models/PackageCollections.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ public bool IsChecked
188188
public bool InstallerHostChanged { get; private set; }
189189
public string InstallerHostChangeTooltip { get; private set; } = "";
190190

191+
public bool InstalledVersionIsUnverified => Package.InstalledVersionIsUnverified;
192+
public string? InstalledVersionTooltip { get; private set; }
193+
191194
public string InstallerHostText { get; private set; } = "";
192195
public string? InstallerHostTooltip { get; private set; }
193196

@@ -219,6 +222,7 @@ public PackageWrapper(IPackage package, PackagesPageViewModel page)
219222
Package = package;
220223
_page = page;
221224
VersionComboString = package.VersionString;
225+
InstalledVersionTooltip = InstalledVersionNotice.BuildTooltip(package);
222226

223227
Package.PropertyChanged += Package_PropertyChanged;
224228
UpdateDisplayState();

src/UniGetUI.Avalonia/ViewModels/DialogPages/PackageDetailsViewModel.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using UniGetUI.Core.Tools;
1111
using UniGetUI.PackageEngine.Enums;
1212
using UniGetUI.PackageEngine.Interfaces;
13+
using UniGetUI.PackageEngine.PackageClasses;
1314

1415
namespace UniGetUI.Avalonia.ViewModels;
1516

@@ -53,6 +54,9 @@ public partial class PackageDetailsViewModel : ObservableObject
5354
[ObservableProperty]
5455
private string _versionDisplay = "";
5556

57+
[ObservableProperty]
58+
private string? _installedVersionTooltip;
59+
5660
[ObservableProperty]
5761
private Uri? _homepageUrl;
5862

@@ -189,9 +193,13 @@ public PackageDetailsViewModel(IPackage package, OperationType role)
189193
MainActionLabel = CoreTools.Translate(
190194
"Update to version {0}", upgradable?.NewVersionString ?? package.NewVersionString);
191195
LabelVersion = CoreTools.Translate("Installed Version");
192-
VersionDisplay = (upgradable?.VersionString ?? package.VersionString)
196+
var updateSource = upgradable ?? package;
197+
VersionDisplay = updateSource.VersionString
193198
+ " ➤ "
194199
+ (upgradable?.NewVersionString ?? package.NewVersionString);
200+
InstalledVersionTooltip = InstalledVersionNotice.BuildTooltip(
201+
updateSource
202+
);
195203
AsAdminLabel = CoreTools.Translate("Update as administrator");
196204
InteractiveLabel = CoreTools.Translate("Interactive update");
197205
SkipHashOrRemoveDataLabel = CoreTools.Translate("Skip hash check");
@@ -201,7 +209,11 @@ public PackageDetailsViewModel(IPackage package, OperationType role)
201209
{
202210
MainActionLabel = CoreTools.Translate("Uninstall");
203211
LabelVersion = CoreTools.Translate("Installed Version");
204-
VersionDisplay = installed?.VersionString ?? package.VersionString;
212+
var uninstallSource = installed ?? package;
213+
VersionDisplay = uninstallSource.VersionString;
214+
InstalledVersionTooltip = InstalledVersionNotice.BuildTooltip(
215+
uninstallSource
216+
);
205217
AsAdminLabel = CoreTools.Translate("Uninstall as administrator");
206218
InteractiveLabel = CoreTools.Translate("Interactive uninstall");
207219
SkipHashOrRemoveDataLabel = CoreTools.Translate("Uninstall and remove data");

src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml.cs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Avalonia.Threading;
1212
using UniGetUI.Avalonia.Infrastructure;
1313
using UniGetUI.Avalonia.ViewModels;
14+
using UniGetUI.Avalonia.Views.Controls;
1415
using UniGetUI.Avalonia.Views.DialogPages;
1516
using UniGetUI.Core.Logging;
1617
using UniGetUI.Core.Tools;
@@ -219,7 +220,7 @@ private void BuildDetailsInlines()
219220

220221
AddInlineRow(DetailsPanel, _vm.LabelPackageId, _vm.PackageId);
221222
AddInlineRow(DetailsPanel, _vm.LabelManifest, _vm.ManifestUrl);
222-
AddInlineRow(DetailsPanel, _vm.LabelVersion, _vm.VersionDisplay);
223+
AddInlineRow(DetailsPanel, _vm.LabelVersion, _vm.VersionDisplay, _vm.InstalledVersionTooltip);
223224

224225
AddSpacer(DetailsPanel);
225226

@@ -280,14 +281,22 @@ private void BuildDetailsInlines()
280281
private static readonly IBrush NotAvailableBrush =
281282
new SolidColorBrush(Color.FromArgb(255, 127, 127, 127));
282283

284+
private static readonly IBrush WarningBrush =
285+
new SolidColorBrush(Color.FromArgb(255, 245, 158, 11));
286+
283287
private static void AddSpacer(StackPanel host) =>
284288
host.Children.Add(new Border { Height = 10 });
285289

286290
/// <summary>
287291
/// Builds a single wrap-able row with "<bold>Label:</bold> value" all on one line,
288292
/// matching the WinUI RichTextBlock paragraph layout.
289293
/// </summary>
290-
private void AddInlineRow(StackPanel host, string label, string value)
294+
private void AddInlineRow(
295+
StackPanel host,
296+
string label,
297+
string value,
298+
string? warningTooltip = null
299+
)
291300
{
292301
var tb = new SelectableTextBlock { TextWrapping = TextWrapping.Wrap };
293302
var inlines = tb.Inlines ??= new InlineCollection();
@@ -300,7 +309,43 @@ private void AddInlineRow(StackPanel host, string label, string value)
300309
});
301310
else
302311
inlines.Add(new Run(value));
303-
host.Children.Add(tb);
312+
313+
if (warningTooltip is null)
314+
{
315+
host.Children.Add(tb);
316+
return;
317+
}
318+
319+
host.Children.Add(BuildWarningRow(tb, warningTooltip));
320+
}
321+
322+
private static Grid BuildWarningRow(Control content, string tooltip)
323+
{
324+
ToolTip.SetTip(content, tooltip);
325+
var icon = new SvgIcon
326+
{
327+
Path = "avares://UniGetUI/Assets/Symbols/warning_filled.svg",
328+
Width = 16,
329+
Height = 16,
330+
Foreground = WarningBrush,
331+
VerticalAlignment = VerticalAlignment.Top,
332+
Margin = new Thickness(6, 2, 0, 0),
333+
};
334+
ToolTip.SetTip(icon, tooltip);
335+
336+
var grid = new Grid
337+
{
338+
ColumnDefinitions =
339+
[
340+
new ColumnDefinition(new GridLength(1, GridUnitType.Star)),
341+
new ColumnDefinition(GridLength.Auto),
342+
],
343+
};
344+
Grid.SetColumn(content, 0);
345+
Grid.SetColumn(icon, 1);
346+
grid.Children.Add(content);
347+
grid.Children.Add(icon);
348+
return grid;
304349
}
305350

306351
private void AddInlineRow(StackPanel host, string label, Uri? url)

src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -617,9 +617,16 @@
617617
<DataTemplate x:DataType="pkg:PackageWrapper">
618618
<Grid ColumnDefinitions="Auto,*" ColumnSpacing="6" VerticalAlignment="Center" Margin="4,0">
619619
<controls:SvgIcon Grid.Column="0" Path="avares://UniGetUI/Assets/Symbols/version.svg"
620-
Width="24" Height="24" VerticalAlignment="Center"/>
620+
Width="24" Height="24" VerticalAlignment="Center"
621+
IsVisible="{Binding !InstalledVersionIsUnverified}"/>
622+
<controls:SvgIcon Grid.Column="0" Path="avares://UniGetUI/Assets/Symbols/warning_filled.svg"
623+
Width="22" Height="22" VerticalAlignment="Center"
624+
Foreground="#F59E0B"
625+
IsVisible="{Binding InstalledVersionIsUnverified}"
626+
ToolTip.Tip="{Binding InstalledVersionTooltip}"/>
621627
<TextBlock Grid.Column="1" Text="{Binding VersionComboString}" VerticalAlignment="Center"
622-
TextTrimming="CharacterEllipsis"/>
628+
TextTrimming="CharacterEllipsis"
629+
ToolTip.Tip="{Binding InstalledVersionTooltip}"/>
623630
</Grid>
624631
</DataTemplate>
625632
</DataGridTemplateColumn.CellTemplate>
@@ -758,7 +765,8 @@
758765
TextTrimming="CharacterEllipsis"/>
759766
<TextBlock Text="{Binding VersionComboString}"
760767
FontSize="11" Opacity="0.5"
761-
TextTrimming="CharacterEllipsis"/>
768+
TextTrimming="CharacterEllipsis"
769+
ToolTip.Tip="{Binding InstalledVersionTooltip}"/>
762770
</StackPanel>
763771
<!-- Col 2: checkbox (top) + overflow button (bottom). The taller card leaves
764772
room to stack them without overlap. -->
@@ -870,7 +878,8 @@
870878
Text="{Binding VersionComboString}"
871879
FontSize="11" Opacity="0.5"
872880
HorizontalAlignment="Center"
873-
TextTrimming="CharacterEllipsis"/>
881+
TextTrimming="CharacterEllipsis"
882+
ToolTip.Tip="{Binding InstalledVersionTooltip}"/>
874883
</Grid>
875884
</Border>
876885
</DataTemplate>

src/UniGetUI.PackageEngine.Interfaces/IPackage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public interface IPackage : INotifyPropertyChanged, IEquatable<IPackage>
2323
/// languages, so it must never be substituted into a command line.
2424
/// </summary>
2525
public bool HasConcreteVersion { get; }
26+
public bool InstalledVersionIsUnverified { get; }
2627
public CoreTools.Version NormalizedVersion { get; }
2728
public CoreTools.Version NormalizedNewVersion { get; }
2829
public IManagerSource Source { get; }

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,9 +349,10 @@ public IReadOnlyList<Package> GetAvailableUpdates_UnSafe()
349349
nativePackage.DefaultInstallVersion.PackageCatalog.Info.Name
350350
);
351351

352+
string? reportedVersion = nativePackage.InstalledVersion.Version;
352353
string version = WinGetPkgOperationHelper.ResolveReportedInstalledVersion(
353354
nativePackage.Id,
354-
nativePackage.InstalledVersion.Version
355+
reportedVersion
355356
);
356357

357358
var UniGetUIPackage = new Package(
@@ -361,7 +362,12 @@ public IReadOnlyList<Package> GetAvailableUpdates_UnSafe()
361362
nativePackage.DefaultInstallVersion.Version,
362363
source,
363364
Manager
364-
);
365+
)
366+
{
367+
InstalledVersionIsUnverified = WinGetPkgOperationHelper.IsUnknownVersion(
368+
reportedVersion
369+
),
370+
};
365371

366372
// Suppress an update that repeatedly fails to stick (#5158); the COM path still avoids
367373
// the one-shot "already upgraded" cache (#5042).
@@ -419,9 +425,10 @@ nativePackage.AvailableVersions is { } versions
419425
source = Manager.GetLocalSource(nativePackage.Id);
420426
}
421427

428+
string? reportedVersion = nativePackage.InstalledVersion.Version;
422429
string version = WinGetPkgOperationHelper.ResolveReportedInstalledVersion(
423430
nativePackage.Id,
424-
nativePackage.InstalledVersion.Version
431+
reportedVersion
425432
);
426433

427434
logger.Log(
@@ -433,7 +440,12 @@ nativePackage.AvailableVersions is { } versions
433440
version,
434441
source,
435442
Manager
436-
);
443+
)
444+
{
445+
InstalledVersionIsUnverified = WinGetPkgOperationHelper.IsUnknownVersion(
446+
reportedVersion
447+
),
448+
};
437449
NativePackageHandler.AddPackage(UniGetUIPackage, nativePackage);
438450
packages.Add(UniGetUIPackage);
439451
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ internal IReadOnlyList<Package> BuildUpdatePackages(ListResponse result)
6565
match.AvailableVersion!,
6666
GetSource(match),
6767
Manager
68-
);
68+
)
69+
{
70+
InstalledVersionIsUnverified = versionUnknown,
71+
};
6972

7073
// Skip one-shot suppression for unknown versions so the restored mark isn't cleared.
7174
if (versionUnknown || !WinGetPkgOperationHelper.ConsumeAlreadyUpgradedSuppression(package))
@@ -99,6 +102,11 @@ public IReadOnlyList<Package> GetInstalledPackages_UnSafe()
99102
GetSource(match),
100103
Manager
101104
)
105+
{
106+
InstalledVersionIsUnverified = WinGetPkgOperationHelper.IsUnknownVersion(
107+
match.InstalledVersion
108+
),
109+
}
102110
)
103111
.ToArray();
104112
}

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

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -475,12 +475,25 @@ private static ShowResult ShowWithRepository(PackageQuery query)
475475
Func<PackageQuery, ShowResult>? showPackage = null
476476
)
477477
{
478+
string? trimmedVersion = TrimTrailingZeroSegments(version);
478479
IReadOnlyList<string>? urls = TryGetInstallerUrlsCore(
479480
package,
480481
version,
481482
requireExactVersion: true,
482-
showPackage
483+
showPackage,
484+
logFailures: trimmedVersion is null
483485
);
486+
487+
if (urls is null && trimmedVersion is not null)
488+
{
489+
urls = TryGetInstallerUrlsCore(
490+
package,
491+
trimmedVersion,
492+
requireExactVersion: true,
493+
showPackage
494+
);
495+
}
496+
484497
if (urls is null)
485498
return null;
486499

@@ -512,11 +525,34 @@ private static ShowResult ShowWithRepository(PackageQuery query)
512525
return TryGetInstallerUrlsCore(package, null, requireExactVersion: false, showPackage);
513526
}
514527

528+
internal static string? TrimTrailingZeroSegments(string version)
529+
{
530+
if (string.IsNullOrWhiteSpace(version))
531+
return null;
532+
533+
string[] segments = version.Split('.');
534+
if (segments.Length < 2)
535+
return null;
536+
537+
foreach (string segment in segments)
538+
{
539+
if (segment.Length == 0 || !segment.All(char.IsAsciiDigit))
540+
return null;
541+
}
542+
543+
int last = segments.Length - 1;
544+
while (last > 0 && segments[last].All(character => character == '0'))
545+
last--;
546+
547+
return last == segments.Length - 1 ? null : string.Join('.', segments[..(last + 1)]);
548+
}
549+
515550
private static IReadOnlyList<string>? TryGetInstallerUrlsCore(
516551
IPackage package,
517552
string? version,
518553
bool requireExactVersion,
519-
Func<PackageQuery, ShowResult>? showPackage
554+
Func<PackageQuery, ShowResult>? showPackage,
555+
bool logFailures = true
520556
)
521557
{
522558
try
@@ -533,10 +569,13 @@ private static ShowResult ShowWithRepository(PackageQuery query)
533569
string returnedVersion = result.Manifest.Version ?? "";
534570
if (!string.Equals(returnedVersion, version, StringComparison.OrdinalIgnoreCase))
535571
{
536-
Logger.Info(
537-
$"Pinget returned manifest version '{returnedVersion}' when '{version}' "
538-
+ $"was requested for {package.Id}; treating as not found"
539-
);
572+
if (logFailures)
573+
{
574+
Logger.Info(
575+
$"Pinget returned manifest version '{returnedVersion}' when '{version}' "
576+
+ $"was requested for {package.Id}; treating as not found"
577+
);
578+
}
540579
return null;
541580
}
542581
}
@@ -555,9 +594,12 @@ private static ShowResult ShowWithRepository(PackageQuery query)
555594
}
556595
catch (Exception ex)
557596
{
558-
Logger.Warn(
559-
$"Could not resolve installer URLs for {package.Id} version {version}: {ex.Message}"
560-
);
597+
if (logFailures)
598+
{
599+
Logger.Warn(
600+
$"Could not resolve installer URLs for {package.Id} version {version}: {ex.Message}"
601+
);
602+
}
561603
return null;
562604
}
563605
}

0 commit comments

Comments
 (0)