diff --git a/doc/ReleaseNotes.md b/doc/ReleaseNotes.md index 6b2029d84d..6339d435bf 100644 --- a/doc/ReleaseNotes.md +++ b/doc/ReleaseNotes.md @@ -47,6 +47,14 @@ The WinGet MCP server's existing tools have been extended with new parameters to The PowerShell module now automatically uses `GH_TOKEN` or `GITHUB_TOKEN` environment variables to authenticate GitHub API requests. This significantly increases the GitHub API rate limit, preventing failures in CI/CD pipelines. Use `-Verbose` to see which token is being used. +### Default priority of installer types + +Installer type selection no longer depends on the order defined on the manifest. Instead, preference is given in this order: +- MSIX +- MSI / Wix / Burn +- Nullsoft / Inno / EXE +- Portable + ## Bug Fixes * Fixed the `useLatest` property in the DSC v3 `Microsoft.WinGet/Package` resource schema to emit a boolean default (`false`) instead of the incorrect string `"false"`. diff --git a/src/AppInstallerCLITests/ManifestComparator.cpp b/src/AppInstallerCLITests/ManifestComparator.cpp index 9a7f08db01..4edbaccaf7 100644 --- a/src/AppInstallerCLITests/ManifestComparator.cpp +++ b/src/AppInstallerCLITests/ManifestComparator.cpp @@ -199,8 +199,8 @@ TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]") ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); - // Only because it is first - RequireInstaller(result, msi); + // Msix is preferred over Msi by the default installer type precedence order + RequireInstaller(result, msix); REQUIRE(inapplicabilities.size() == 0); } SECTION("MSI Installed") @@ -238,7 +238,7 @@ TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]") ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); - // Only because it is first + // Burn is preferred over Exe by the default installer type precedence order RequireInstaller(result, burn); REQUIRE(inapplicabilities.size() == 0); } @@ -855,6 +855,31 @@ TEST_CASE("ManifestComparator_InstallerType", "[manifest_comparator]") REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType }); } + SECTION("No user preference - default order applies") + { + // No TestUserSettings means no user-configured preferences; the default order should be used. + // Default order: MSStore > Msix > Msi > Wix > Burn > Nullsoft > Inno > Exe > Portable + // Manifest has Msi, Exe, Msix — Msix should win. + ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); + auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); + + RequireInstaller(result, msix); + REQUIRE(inapplicabilities.size() == 0); + } + SECTION("No user preference - type not in default list does not win over default-ordered types") + { + // Zip is not in the default preference list; it should not be preferred over Msix/Msi/Exe. + Manifest localManifest; + ManifestInstaller zip = AddInstaller(localManifest, Architecture::Neutral, InstallerTypeEnum::Zip, ScopeEnum::User); + ManifestInstaller localExe = AddInstaller(localManifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::User); + + ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); + auto [result, inapplicabilities] = mc.GetPreferredInstaller(localManifest); + + // Exe is in the default list; Zip is not — Exe should be preferred. + RequireInstaller(result, localExe); + REQUIRE(inapplicabilities.size() == 0); + } } TEST_CASE("ManifestComparator_MachineArchitecture_Strong_Scope_Weak", "[manifest_comparator]") diff --git a/src/AppInstallerCommonCore/Manifest/ManifestComparator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestComparator.cpp index 92bd27f0aa..6a5bf44758 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestComparator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestComparator.cpp @@ -228,6 +228,22 @@ namespace AppInstaller::Manifest { preference = Settings::User().Get(); requirement = Settings::User().Get(); + + // Apply default precedence order when the user has not configured any installer type preferences or requirements. + if (preference.empty() && requirement.empty()) + { + preference = { + InstallerTypeEnum::MSStore, + InstallerTypeEnum::Msix, + InstallerTypeEnum::Msi, + InstallerTypeEnum::Wix, + InstallerTypeEnum::Burn, + InstallerTypeEnum::Nullsoft, + InstallerTypeEnum::Inno, + InstallerTypeEnum::Exe, + InstallerTypeEnum::Portable, + }; + } } if (!preference.empty() || !requirement.empty())