Skip to content

Skip installers already on disk and make bundle downloads resolve a real version - #5469

Merged
Gabriel Dufresne (GabrielDuf) merged 3 commits into
mainfrom
fix/5422-installer-download-skip-and-version
Oct 2, 2026
Merged

Gabriel Dufresne (GabrielDuf) merged 3 commits into
mainfrom
fix/5422-installer-download-skip-and-version

Conversation

@GabrielDuf

@GabrielDuf Gabriel Dufresne (GabrielDuf) commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Three gaps in the installer download flow, all reported in #5422.

An unpinned bundle entry could not be downloaded at all

ImportedPackage returns the localized "Latest" placeholder from VersionString, and the managers whose installer URL follows the package version fed that straight into their version-keyed lookups. Against the real Chocolatey feed:

Packages(Id='7zip',Version='Latest')   -> HTTP 404 "Resource not found"
Packages(Id='7zip',Version='26.3.0')   -> HTTP 200, src=".../package/7zip/26.3.0"

No installer URL came back, so the operation reported that no installer exists — the reporter's "download installer sometimes not work", and it is specific to the bundle page because that is the only place a package has no concrete version. BaseNuGetDetailsHelper now resolves the newest installable version before the lookup and threads it through all three paths; Cargo falls back to max_stable_version.

Selection goes through NuGetV3Client.SelectHighestVersion, which parses real SemVer and prefers a stable release, so the download matches what installing the same bundle entry would get. The V2 listing is sorted ordinally, so a digit-collecting comparison would also have picked 9.0 over 10.0.

The downloaded file carried no version for those entries

The version was read off what the UI renders rather than what the manifest supplying the installer URL reports, so a placeholder produced a bare name. IPackageDetails now carries the version the installer URL belongs to, populated by every manager that knows it, and ResolveInstallerVersion prefers it. It is ignored unless it contains a digit, so Unknown and other placeholders fall through to the existing chain instead of suppressing it.

WinGet reads it only from the corrected in-process manifest: the bundled CLI exposes the collapsed manifest document, whose PackageVersion pinget itself treats as unreliable and overwrites from the catalog.

An installer already present was downloaded again

PublishedInstallerHash parses every shape the managers publish and hashes the existing file with the matching algorithm:

Format Managers
bare hex (MD5 … SHA-512) WinGet, Pip, Cargo, Scoop
algorithm:hex Scoop
SRI algorithm-base64 npm, Bun
bare base64 Chocolatey, .NET, PowerShell

A digest whose length contradicts its stated algorithm is refused rather than guessed at. A match skips the download; anything else downloads as before, so a corrupted or stale file is still replaced.

Failures are also no longer silent — the two bundle download entry points reported only to the log, which is what made this look intermittent.

Verification

Each manager's real published hash was checked against the real downloaded artifact (npm's SRI vs express-5.2.1.tgz, Chocolatey's base64 SHA-512 vs 7zip.26.3.0.nupkg, pip's hex vs the requests wheel), and the bundle path was driven end to end against the live NuGet feed:

HasConcreteVersion = False
VersionString      = 'Latest'
  Download URL found at https://api.nuget.org/v3-flatcontainer/dotnetsay/3.0.3/dotnetsay.3.0.3.nupkg
  The file was saved to ...\Dotnetsay_3.0.3.nupkg
VERDICT = Succeeded

SECOND RUN = Succeeded
  ... matches the Sha512 hash published for this installer, the download was skipped

The same probe on the pre-change code reports VERDICT = Failed — "UniGetUI was not able to find any installer for this package."

Downloads were also run through the app itself headless over the IPC API: first fetch, re-run skipped on the hash, file corrupted by hand and correctly re-downloaded, and Dotnetsay_2.1.7.nupkg once a versioned naming scheme was selected.

3615 tests pass, with 22 added. dotnet format whitespace src --folder --verify-no-changes exits 0, and the translation validation scripts pass.

Deliberately left out

  • A download's own bytes are still not verified after the transfer; the digest is in hand, but making downloads hard-fail on a hash mismatch is a policy change that deserves its own issue.
  • Prerelease resolution ignores the manager's PreRelease option, which BaseNuGet honours elsewhere. Not touched here, since the issue does not mention it.
  • Bun is patched by analogy with npm and could not be run (not installed on the dev machine); if its info --json lacks a root version, naming falls back to previous behaviour rather than producing anything wrong.

Closes #5422

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation is well covered; the remaining XML documentation issue is non-blocking.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

Improves installer downloads by resolving concrete versions, naming artifacts correctly, and skipping verified existing files.

Changes:

  • Resolves unpinned NuGet/Cargo versions and propagates manifest versions.
  • Parses published hashes to avoid redundant downloads.
  • Adds user-visible failures and comprehensive tests.
File Description
PublishedInstallerHashTests.cs Tests supported hash formats.
NuGetV3ClientTests.cs Tests stable-version selection.
NuGetManifestLoaderTests.cs Tests version-aware cache keys.
InstallerFileNameTests.cs Tests resolved-version naming.
TestPackageDetailsHelper.cs Copies manifest versions.
DownloadOperationProgressTests.cs Tests hash-based download skipping.
Package.cs Uses resolved installer versions in filenames.
PackageDetails.cs Stores installer manifest version.
PublishedInstallerHash.cs Parses and verifies published hashes.
DownloadOperation.cs Skips matching existing installers.
PingetPackageDetailsProvider.cs Exposes WinGet manifest version.
ScoopPkgDetailsHelper.cs Exposes Scoop manifest version.
PipPkgDetailsHelper.cs Exposes PyPI version.
NpmPkgDetailsHelper.cs Exposes npm version.
NuGetManifestLoader.cs Adds version-aware URLs and caching.
BaseNuGetDetailsHelper.cs Resolves concrete NuGet versions.
CargoPkgDetailsHelper.cs Resolves unpinned Cargo versions.
BunPkgDetailsHelper.cs Exposes Bun package version.
IPackageDetails.cs Adds the installer version contract.
AvaloniaPackageOperationHelper.cs Surfaces download failures to users.
lang_en.json Adds notification translations.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

NuGet V3 resolution can select an unlisted or withdrawn version from the flat-container feed.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Exclude unlisted versions when selecting the latest installable release

src/​UniGetUI.PackageEngine.Managers.Generic.NuGet/​BaseNuGetDetailsHelper.cs:431

For a V3 source, GetInstallableVersions_UnSafe reads the flat-container version list, which includes withdrawn/unlisted versions. Selecting its semantic maximum directly can therefore make an unpinned bundle download an unlisted release rather than the latest installable release. The existing update/search path explicitly filters candidates through SelectNewestNotUnlisted (NuGetV3Client.cs:434-483); this resolution path should use the same listed-aware selection before loading details.

@randy-but-a-ro randy-but-a-ro Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Pull request was approved automatically: the AI review is complete and all its review threads are resolved. 🎉

Integration Details
{
	"deliveryId": "3a174a20-be97-11f1-80c7-05dd358fa81b",
	"headSha": "4d94862ac5800cc9d443e6a5724eed1e1b9d6a1e",
	"reviewer": "copilot-pull-request-reviewer[bot]"
}

@GabrielDuf
Gabriel Dufresne (GabrielDuf) merged commit b1eb136 into main Oct 2, 2026
7 checks passed
@GabrielDuf
Gabriel Dufresne (GabrielDuf) deleted the fix/5422-installer-download-skip-and-version branch October 2, 2026 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Updated- Download Installer - Need Improvement for Naming Installer Example "PackageName_Version"

2 participants