diff --git a/doc/windows/package-manager/winget/returnCodes.md b/doc/windows/package-manager/winget/returnCodes.md index 1ec122dcef..8d7de20b4c 100644 --- a/doc/windows/package-manager/winget/returnCodes.md +++ b/doc/windows/package-manager/winget/returnCodes.md @@ -153,6 +153,8 @@ ms.localizationpriority: medium | 0x8A15008B | -1979335093 | APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED | Font uninstall failed. The font may not be in a good state. Try uninstalling after a restart. | | 0x8A15008C | -1979335092 | APPINSTALLER_CLI_ERROR_FONT_VALIDATION_FAILED | Font validation failed. | | 0x8A15008D | -1979335091 | APPINSTALLER_CLI_ERROR_FONT_ROLLBACK_FAILED | Font rollback failed. The font may not be in a good state. Try uninstalling after a restart. | +| 0x8A15008E | -1979335090 | APPINSTALLER_CLI_ERROR_UPDATE_INSTALL_TECHNOLOGY_MISMATCH | An upgrade is available but uses a different install technology than the current installation | +| 0x8A15008F | -1979335089 | APPINSTALLER_CLI_ERROR_UPGRADE_INSTALLED_VERSION_MISMATCH | Installer reported success but the installed version is still older than the upgrade target | ## Install errors. diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index ef3db8b395..c62797e86f 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -796,6 +796,8 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeDifferentInstallTechnology); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeDifferentInstallTechnologyInNewerVersions); + WINGET_DEFINE_RESOURCE_STRINGID(UpgradeInstallTechnologyMismatchCount); + WINGET_DEFINE_RESOURCE_STRINGID(UpgradeInstalledVersionMismatch); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeIsPinned); WINGET_DEFINE_RESOURCE_STRINGID(UpgradePinnedByUserCount); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeRequireExplicitCount); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index f702629c20..38e68f9b67 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" +#include #include "InstallFlow.h" #include "DownloadFlow.h" #include "FontFlow.h" @@ -20,6 +21,7 @@ #include "SourceFlow.h" #include #include +#include #include #include #include @@ -59,6 +61,36 @@ namespace AppInstaller::CLI::Workflow } } + // After a successful installer exit code, confirms ARP/MSIX reports at least the manifest version (upgrade only). + void VerifyUpgradeInstalledVersion( + Execution::Context& context, + std::string_view installedVersionString, + const Manifest::Manifest& manifest) + { + if (!WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate)) + { + return; + } + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::RebootRequired)) + { + return; + } + + Version expectedVersion{ manifest.Version }; + Version installedVersion{ std::string{ installedVersionString } }; + if (expectedVersion.IsUnknown() || installedVersion.IsUnknown()) + { + return; + } + if (installedVersion < expectedVersion) + { + context.Reporter.Error() << Resource::String::UpgradeInstalledVersionMismatch( + Utility::LocIndString{ std::string{ manifest.Version } }, + Utility::LocIndString{ std::string{ installedVersionString } }) << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPGRADE_INSTALLED_VERSION_MISMATCH); + } + } + bool ShouldUseDirectMSIInstall(InstallerTypeEnum type, bool isSilentInstall) { switch (type) @@ -886,6 +918,11 @@ namespace AppInstaller::CLI::Workflow if (installer && !installer->PackageFamilyName.empty() && Deployment::IsRegistered(installer->PackageFamilyName)) { + const auto& manifest = context.Get(); + if (auto installedVersion = Deployment::GetInstalledVersionStringForFamilyName(installer->PackageFamilyName)) + { + VerifyUpgradeInstalledVersion(context, *installedVersion, manifest); + } return; } @@ -898,6 +935,11 @@ namespace AppInstaller::CLI::Workflow // Store the ARP entry found to match the package to record it in the tracking catalog later if (correlationResult.Package) { + VerifyUpgradeInstalledVersion( + context, + correlationResult.Package->GetProperty(PackageVersionProperty::Version).get(), + manifest); + std::vector entries; auto metadata = correlationResult.Package->GetMetadata(); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 6cfee598b7..d9a3c264fa 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1345,6 +1345,14 @@ Please specify one of them using the --source option to proceed. A newer version was found, but the install technology is different from the current version installed. Please uninstall the package and install the newer version. + + {0} package(s) have upgrades blocked because newer versions use a different install technology than the current installation. Uninstall each package, then install the newer version. + {Locked="{0}"} {0} is the number of packages skipped for this reason during winget upgrade --all. + + + The installer reported success, but the installed version is still {1}. The upgrade target was {0}. + {Locked="{0}","{1}"} {0} is the expected package version from the manifest; {1} is the version reported by Windows after install. + The install technology of the newer version specified is different from the current version installed. Please uninstall the package and install the newer version. @@ -2265,6 +2273,12 @@ Please specify one of them using the --source option to proceed. No applicable update found + + An upgrade is available but uses a different install technology than the current installation + + + Installer reported success but the installed version is still older than the upgrade target + winget upgrade --all completed with failures diff --git a/src/AppInstallerCommonCore/Deployment.cpp b/src/AppInstallerCommonCore/Deployment.cpp index 3824d21644..f5261d2945 100644 --- a/src/AppInstallerCommonCore/Deployment.cpp +++ b/src/AppInstallerCommonCore/Deployment.cpp @@ -385,6 +385,30 @@ namespace AppInstaller::Deployment return packages.begin() != packages.end(); } + std::optional GetInstalledVersionStringForFamilyName(std::string_view packageFamilyName) + { + try + { + std::wstring wideFamilyName = Utility::ConvertToUTF16(packageFamilyName); + + PackageManager packageManager; + auto packages = packageManager.FindPackagesForUser({}, wideFamilyName); + auto it = packages.begin(); + if (it == packages.end()) + { + return std::nullopt; + } + + const auto& v = it->Id().Version(); + std::ostringstream stream; + stream << v.Major << '.' << v.Minor << '.' << v.Build << '.' << v.Revision; + return stream.str(); + } + CATCH_LOG(); + + return std::nullopt; + } + void RegisterPackage( std::string_view packageFamilyName, IProgressCallback& callback) diff --git a/src/AppInstallerCommonCore/Public/AppInstallerDeployment.h b/src/AppInstallerCommonCore/Public/AppInstallerDeployment.h index 68f16b80e8..422efbe215 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerDeployment.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerDeployment.h @@ -2,6 +2,9 @@ // Licensed under the MIT License. #pragma once #include +#include +#include +#include #include #include @@ -61,6 +64,9 @@ namespace AppInstaller::Deployment // Calls winrt::Windows::Management::Deployment::PackageManager::FindPackagesForUser bool IsRegistered(std::string_view packageFamilyName); + // Returns the version string (major.minor.build.revision) for the first package found for the family name, if any. + std::optional GetInstalledVersionStringForFamilyName(std::string_view packageFamilyName); + // Calls winrt::Windows::Management::Deployment::PackageManager::RegisterPackageByFamilyNameAsync void RegisterPackage( std::string_view packageFamilyName, diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp index e3fdd13f1d..c6e27636cb 100644 --- a/src/AppInstallerSharedLib/Errors.cpp +++ b/src/AppInstallerSharedLib/Errors.cpp @@ -131,6 +131,8 @@ namespace AppInstaller WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_FAILURE, "Manifest validation failed"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, "Manifest is invalid"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE, "No applicable update found"), + WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UPDATE_INSTALL_TECHNOLOGY_MISMATCH, "An upgrade is available but uses a different install technology than the current installation"), + WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UPGRADE_INSTALLED_VERSION_MISMATCH, "Installer reported success but the installed version is still older than the upgrade target"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE, "winget upgrade --all completed with failures"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED, "Installer failed security check"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH, "Download size does not match expected content length"), diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h index 2bb4ad097e..2ee49ea6a5 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -162,6 +162,8 @@ #define APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED ((HRESULT)0x8A15008B) #define APPINSTALLER_CLI_ERROR_FONT_VALIDATION_FAILED ((HRESULT)0x8A15008C) #define APPINSTALLER_CLI_ERROR_FONT_ROLLBACK_FAILED ((HRESULT)0x8A15008D) +#define APPINSTALLER_CLI_ERROR_UPDATE_INSTALL_TECHNOLOGY_MISMATCH ((HRESULT)0x8A15008E) +#define APPINSTALLER_CLI_ERROR_UPGRADE_INSTALLED_VERSION_MISMATCH ((HRESULT)0x8A15008F) // Install errors. #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) diff --git a/src/Microsoft.Management.Deployment/Converters.h b/src/Microsoft.Management.Deployment/Converters.h index 460fe4e21c..4c3f724e8c 100644 --- a/src/Microsoft.Management.Deployment/Converters.h +++ b/src/Microsoft.Management.Deployment/Converters.h @@ -82,6 +82,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation WINGET_GET_OPERATION_RESULT_STATUS(NoApplicableInstallers, InternalError, NoApplicableInstallers, NoApplicableRepairer); break; case APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE: + case APPINSTALLER_CLI_ERROR_UPDATE_INSTALL_TECHNOLOGY_MISMATCH: case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN: case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER: WINGET_GET_OPERATION_RESULT_STATUS(NoApplicableUpgrade, InternalError, InternalError, InternalError); @@ -97,6 +98,9 @@ namespace winrt::Microsoft::Management::Deployment::implementation case APPINSTALLER_CLI_ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED: WINGET_GET_OPERATION_RESULT_STATUS(InternalError, InternalError, InternalError, RepairError); break; + case APPINSTALLER_CLI_ERROR_UPGRADE_INSTALLED_VERSION_MISMATCH: + WINGET_GET_OPERATION_RESULT_STATUS(InstallError, InternalError, InternalError, InternalError); + break; case APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED: WINGET_GET_OPERATION_RESULT_STATUS(PackageAgreementsNotAccepted, InternalError, PackageAgreementsNotAccepted, PackageAgreementsNotAccepted); break;