Repository navigation
Invalidate a manager's installed-package caches when an operation succeeds - #5586
Merged
Gabriel Dufresne (GabrielDuf) merged 1 commit intoOct 23, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
NativeAOT publish validation and cross-platform build coverage remain unverified and require human sign-off.
No open issues 🎉
What changed in this PR
This PR addresses #5362 by invalidating installed-package caches after successful operations, allowing the existing Installed Packages refresh to retrieve current versions.
Changes:
- Adds a thread-safe, per-manager cache generation counter and invalidation contract.
- Invalidates caches before success handling and incorporates the counter into WinGet, Scoop, and NuGet-backed cache keys.
- Adds regression tests for successful-operation invalidation and fresh WinGet snapshots.
| File | Description |
|---|---|
src/UniGetUI.Tests/CatalogEditorTests.cs |
Updates the test manager for the new interface. |
src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs |
Tests snapshot refresh after invalidation. |
src/UniGetUI.PackageEngine.Tests/PackageOperationsTests.cs |
Tests invalidation after a successful update. |
src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/PackageManager.cs |
Implements atomic cache generation tracking. |
src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/NullPackageManager.cs |
Adds no-op invalidation support. |
src/UniGetUI.PackageEngine.Operations/PackageOperations.cs |
Invalidates before success handlers run. |
src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs |
Keys snapshots on source and installed generations. |
src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs |
Keys installed listings on the generation counter. |
src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs |
Prevents reuse of in-progress listings from earlier generations. |
src/UniGetUI.PackageEngine.Interfaces/IPackageManager.cs |
Defines the generation and invalidation contract. |
Review effort: Balanced
There was a problem hiding this comment.
🤖 Pull request was approved automatically: the AI review is complete and all its review threads are resolved. 🎉
Integration Details
{
"deliveryId": "783717c0-cef2-11f1-80c0-adf720254145",
"headSha": "15364036446bd4f4db3a6b564c2b4196d6f8bac5",
"reviewer": "copilot-pull-request-reviewer[bot]"
}
Gabriel Dufresne (GabrielDuf)
deleted the
fix/installed-version-stale-after-update
branch
October 23, 2026 16:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
After an update, the Installed Packages tab can keep showing the version that was just replaced until you reload it manually (0.5.3 after updating to 1.1 in the issue).
The refresh the issue asks for already exists.
UpdatePackageOperation.HandleSuccessremoves the row and re-adds whateverManager.GetInstalledPackages()returns. That call is fed stale data: several managers answer from aTaskRecyclerresult taken before the operation finished.BaseNuGet)When the list predates the update,
ResolveInstalledPackageSnapshotAsyncfinds no row at the new version. It falls back to the highest match, which is the old version. Update all hits this reliably, because every package after the first finishes inside the first one's 15 s window.Cargo was already fine: its operation helper clears its own cache when an operation succeeds.
What changed
IPackageManager/PackageManager: a per-managerInstalledPackagesGenerationcounter andInvalidateInstalledPackages().NullPackageManagerand theCatalogEditorTestsfake get no-op versions.PackageOperation:OperationSucceededcallsInvalidateInstalledPackages()beforeHandleSuccess. This is the one success point for install, update and uninstall operations, whether they run through the CLI, the broker or Cargo's managed installer. The event is raised synchronously, so the counter moves before anything re-lists. Downloads and pre/post commands don't bump it.The installed-listing caches now include the counter in their key:
BaseNuGet: keyed on the installed counter.A listing that starts after a successful operation is therefore always fresh. Cargo keeps its existing invalidation.
Verification
New tests:
NativeWinGetHelperTakesAFreshSnapshotOnceTheInstalledPackagesAreInvalidatedASuccessfulOperationInvalidatesTheInstalledPackagesOfItsManager, which runs a real successful update operation.Both fail with the fix removed.
UniGetUI.PackageEngine.TestsandUniGetUI.Testspass.dotnet format whitespace src --folderanddotnet format style src/UniGetUI.Windows.slnxboth exit 0 with--verify-no-changes.Full rebuilds of
UniGetUI.Windows.slnxbefore and after: no new warnings in any changed file.In the app: a WinGet update refreshed the Installed tab to the new version without a manual reload.
TaskRecyclerbuilds its keys from the delegate's hash code. On both CoreCLR and NativeAOT (.NET 10) that hash includes the delegate's target, so Scoop and the four NuGet managers each keep their own cache entry, as before.Notes for reviewers
scoop listfor Scoop. The NuGet managers barely change, since they already listed fresh except when joining a listing in progress.HandleSuccessruns in the background, so the queue doesn't wait on it.UniGetUI.Avalonia.slnxbuild, and a NativeAOT publish. The change uses no reflection or runtime code generation: delegate calls and one closed generic over a value tuple.Closes #5362