Skip to content

Conversation

@AmelBawa-msft
Copy link
Contributor

@AmelBawa-msft AmelBawa-msft commented Sep 8, 2025

  • Register for the user (when appropriate) after Repair -AllUsers

    Register AppInstaller when using AddProvisionPackageAsync which was not registering the package prior to this PR change.

  • Ensure that Repair also installs the winget-cli source package

    Added a "best effort" approach to programmatically attempt to downloading the source2.msix and install it.

  • Fix Win32Exception An error occurred trying to start process 'winget.exe' error the first time Repair -AllUsers is run

    Keeping this as-is since the code includes a descriptive message. In my repro, the issue occurred when App Installer was not installed, and the code attempted to invoke winget.exe to check if the application was present.

  • Repair -Force should bypass checks and attempt to install everything

    This is not addressed in this PR. It's unclear which constraints should be relaxed, as the current implementation already attempts to ensure the requested parameters are satisfied. The Force flag is currently used to trigger ForceTargetApplicationShutdown during package addition, which appears appropriate. I recommend opening a separate issue to propose and discuss the desired behavior.

  • I'd like to be able to use Repair-WinGetPackageManager -Version 1.11.* to get the latest version of WinGet 1.11 that is not a pre-release version.

    Added support for leading and trailing wildcard: 1.2.*, 1.2*, 1.*2.3

Related issues:

Microsoft Reviewers: Open in CodeFlow

// WinGet Source
private const string WinGetSourceName = "Microsoft.Winget.Source";
private const string WinGetSourceMsixName = "source2.msix";
private const string WinGetSourceUrl = "https://cdn.winget.microsoft.com/cache/source2.msix";
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a COM API for fetching source information? Since the winget source is a well-known source (can be disabled, but not removed), would it be a possible consideration to dynamically fetch the source URL through the API? That way, in the event a source3.msix ever becomes the default, this portion of the cmdlet would just work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was able to find the CDN URL https://cdn.winget.microsoft.com/cache but not the source2.msix file as part of the COM API. @JohnMcPMS, is there a recommended/forward compatible method to obtain the source2.msix URL instead of hardcoding it in the powershell module? Or specifying it here is okay?

Copy link
Member

Choose a reason for hiding this comment

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

It is an internal implementation detail, so there isn't a way to get the file name. I don't see why we need to specify it here twice though.

Copy link
Member

Choose a reason for hiding this comment

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

Can we not have the file name twice? Use string concatenation or formatting or whatever to only have source2.msix the one time.

@AmelBawa-msft AmelBawa-msft marked this pull request as ready for review September 9, 2025 19:51
return partPattern == partValue.ToString();
}

private async Task<string> ResolveVersionAsync(string version, bool includePrerelease)
Copy link
Member

Choose a reason for hiding this comment

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

This function and its helpers should be members of Microsoft.WinGet.Client.Engine.Helpers.GitHubClient like GetLatestReleaseTagNameAsync is.

Also, it should have a heuristic looking for wildcards before invoking a bunch of web calls and shortcut return the input version if no wildcard is present.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, I’ve moved that logic into the helper class.
We’re only making a single GetAll() web request to resolve the version, which I think is sufficient.

Copy link
Member

Choose a reason for hiding this comment

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

I fully disagree, don't make any web request when we don't need one.

Copy link
Member

Choose a reason for hiding this comment

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

I think if you moved functionality for the matching to WinGetVersion, you could easily have something like:

if (!WinGetVersion.VersionHasWildcard(version))
{
    return version;
}

VersionPartMatch(minor, v.Version.Minor) &&
VersionPartMatch(build, v.Version.Build) &&
VersionPartMatch(revision, v.Version.Revision))
.OrderBy(f => f.Version);
Copy link
Member

Choose a reason for hiding this comment

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

In the current GetLatestReleaseAsync, we assume that the releases are returned in a sorted order (latest firstest). If that is true and maintained by all of the LINQ queries, then a First should be sufficient and optimal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don’t think “latest release” necessarily means “latest version,” since you could technically release version 1.0.3 before 1.2.9. Releases are probably sorted by their creation date, which makes sense for identifying the latest release. But for this algorithm, it’s key to make sure the version list is sorted semantically, not just by release date.

Copy link
Member

Choose a reason for hiding this comment

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

Then we should update GetLatestReleaseAsync to match this understanding of semantics. It is definitely not intended to be latest in terms of calendar, but latest in terms of the highest version. Fortunately, these are the same sort order for release only listing. When pre-release is involved, they could become interleaved, as the latest calendar date might be a release with a lower version number than a pre-release from before.

The documentation for listing releases does not mention ANY ordering, so even assuming calendar is faulty.

I would suggest making GetAllReleasesAsync return a collection of some type containing both a WinGetVersion and the Release, sorted so that the highest version is first. This makes the existing "latest" function more correct and your new function more efficient as it can simply get the First object matching the predicate rather than examining all of them.

Copy link
Contributor

Choose a reason for hiding this comment

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

This apparently is a bug right now, actually, since the latest Pre-Release is 1.12.240-preview, but using Repair-WinGetPackageManager -IncludePrerelease -Latest selects 1.11.510

Would be really nice to have this fixed

Choose a reason for hiding this comment

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

I just faced the same issue tonight. I believe my winget was previously on 1.11.x but when running Repair-WinGetPackageManager -Force -IncludePrerelease -Verbose it wasn't getting the pre-release version. After stumbling upon what you wrote, I manually specified the latest pre-release version and passed it in alongside -Version and it finally worked.

// WinGet Source
private const string WinGetSourceName = "Microsoft.Winget.Source";
private const string WinGetSourceMsixName = "source2.msix";
private const string WinGetSourceUrl = "https://cdn.winget.microsoft.com/cache/source2.msix";
Copy link
Member

Choose a reason for hiding this comment

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

It is an internal implementation detail, so there isn't a way to get the file name. I don't see why we need to specify it here twice though.

/// <param name="includePrerelease">Include prerelease versions.</param>
/// <param name="result">The resulting version.</param>
/// <returns>True if a matching version was found.</returns>
private static bool TryGetLatestMatchingVersion(IEnumerable<WinGetVersion> versions, string pattern, bool includePrerelease, out WinGetVersion result)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
private static bool TryGetLatestMatchingVersion(IEnumerable<WinGetVersion> versions, string pattern, bool includePrerelease, out WinGetVersion result)
private static bool TryGetLatestMatchingVersion(IEnumerable<WinGetVersion> versions, string pattern, bool includePrerelease, out WinGetVersion? result)

Because you very clearly return a null in some cases. You can annotate with the appropriate attribute to mark it as not null when returning true.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants