diff --git a/doc/release-notes/1.31/6516-upgrade-all-skip-reason.md b/doc/release-notes/1.31/6516-upgrade-all-skip-reason.md
new file mode 100644
index 0000000000..5f512d848b
--- /dev/null
+++ b/doc/release-notes/1.31/6516-upgrade-all-skip-reason.md
@@ -0,0 +1,5 @@
+## Bug Fixes
+
+### Minor Bug Fixes
+
+* `winget upgrade --all` now reports how many available upgrades were skipped because they do not apply to the selected scope or other requirements.
\ No newline at end of file
diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h
index 4b574f5155..d7e0f4fdd9 100644
--- a/src/AppInstallerCLICore/Resources.h
+++ b/src/AppInstallerCLICore/Resources.h
@@ -821,6 +821,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeIsPinned);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradePinnedByUserCount);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeRequireExplicitCount);
+ WINGET_DEFINE_RESOURCE_STRINGID(UpgradeNotApplicableCount);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionCount);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionExplanation);
WINGET_DEFINE_RESOURCE_STRINGID(UriNotWellFormed);
diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp
index a2c243673e..46df0e6c76 100644
--- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp
+++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp
@@ -217,6 +217,7 @@ namespace AppInstaller::CLI::Workflow
int packagesWithUnknownVersionSkipped = 0;
int packagesThatRequireExplicitSkipped = 0;
int packagesSkippedInstallTechnologyMismatch = 0;
+ int packagesSkippedNotApplicable = 0;
for (const auto& match : matches)
{
@@ -253,6 +254,7 @@ namespace AppInstaller::CLI::Workflow
if (updateContext.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE)
{
+ ++packagesSkippedNotApplicable;
continue;
}
@@ -314,6 +316,12 @@ namespace AppInstaller::CLI::Workflow
AICLI_LOG(CLI, Info, << packagesSkippedInstallTechnologyMismatch << " package(s) skipped due to install technology mismatch");
context.Reporter.Info() << Resource::String::UpgradeInstallTechnologyMismatchCount(packagesSkippedInstallTechnologyMismatch) << std::endl;
}
+
+ if (packagesSkippedNotApplicable > 0)
+ {
+ AICLI_LOG(CLI, Info, << packagesSkippedNotApplicable << " package(s) skipped because the available upgrade was not applicable");
+ context.Reporter.Info() << Resource::String::UpgradeNotApplicableCount(packagesSkippedNotApplicable) << std::endl;
+ }
}
void SelectSinglePackageVersionForInstallOrUpgrade::operator()(Execution::Context& context) const
diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
index af04b9f319..5f7330c69f 100644
--- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
+++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
@@ -1429,6 +1429,10 @@ Please specify one of them using the --source option to proceed.
{0} package(s) are pinned and need to be explicitly upgraded.
{Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades.
+
+ {0} package(s) were skipped because an available upgrade does not apply to the selected scope or other requirements.
+ {Locked="{0}"} {0} is the number of packages skipped for this reason during winget upgrade --all.
+
The arguments provided can only be used with a query.
diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
index 07f7dca337..e98f7dec92 100644
--- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
+++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
@@ -760,6 +760,9 @@
true
+
+ true
+
true
diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
index 0311271140..0c3e5139f1 100644
--- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
+++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
@@ -822,6 +822,9 @@
TestData
+
+ TestData
+
TestData
diff --git a/src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_MachineScope.yaml b/src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_MachineScope.yaml
new file mode 100644
index 0000000000..2c3f6d6f25
--- /dev/null
+++ b/src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_MachineScope.yaml
@@ -0,0 +1,19 @@
+# Update manifest with only a machine-scope installer.
+Id: AppInstallerCliTest.TestExeInstaller
+Version: 2.0.0.0
+Name: AppInstaller Test Installer Machine Scope
+Publisher: Microsoft Corporation
+AppMoniker: AICLITestExeMachineScope
+License: Test
+Switches:
+ Custom: /custom
+ SilentWithProgress: /silentwithprogress
+ Silent: /silence
+ Update: /update
+Installers:
+ - Arch: x64
+ Url: https://ThisIsNotUsed
+ InstallerType: exe
+ Scope: machine
+ Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B
+ManifestVersion: 0.1.0
\ No newline at end of file
diff --git a/src/AppInstallerCLITests/UpdateFlow.cpp b/src/AppInstallerCLITests/UpdateFlow.cpp
index 9d70283c78..a4bb811cda 100644
--- a/src/AppInstallerCLITests/UpdateFlow.cpp
+++ b/src/AppInstallerCLITests/UpdateFlow.cpp
@@ -540,6 +540,7 @@ TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]")
OverrideForMSStore(context, true);
OverrideForPortableInstall(context);
context.Args.AddArg(Execution::Args::Type::All);
+ context.Args.AddArg(Execution::Args::Type::InstallScope, "user"sv);
UpgradeCommand update({});
update.Execute(context);
@@ -556,6 +557,22 @@ TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]")
REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath()));
}
+TEST_CASE("UpdateFlow_UpdateAll_ReportsNotApplicable", "[UpdateFlow][workflow]")
+{
+ std::ostringstream updateOutput;
+ TestContext context{ updateOutput, std::cin };
+ auto previousThreadGlobals = context.SetForCurrentThread();
+ OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_NotApplicable }));
+ context.Args.AddArg(Execution::Args::Type::All);
+ context.Args.AddArg(Execution::Args::Type::InstallScope, "user"sv);
+
+ UpgradeCommand update({});
+ update.Execute(context);
+ INFO(updateOutput.str());
+
+ REQUIRE(updateOutput.str().find(Resource::String::UpgradeNotApplicableCount(1)) != std::string::npos);
+}
+
TEST_CASE("UpdateFlow_UpdateAll_IncludeUnknown", "[UpdateFlow][workflow]")
{
TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt");
diff --git a/src/AppInstallerCLITests/WorkflowCommon.cpp b/src/AppInstallerCLITests/WorkflowCommon.cpp
index 93a42282a4..b80beca475 100644
--- a/src/AppInstallerCLITests/WorkflowCommon.cpp
+++ b/src/AppInstallerCLITests/WorkflowCommon.cpp
@@ -276,6 +276,22 @@ namespace TestCommon
PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller")));
});
+ const TestSourceResult TestInstaller_Exe_NotApplicable(
+ "TestExeInstallerNotApplicable"sv,
+ [](std::vector& matches, std::weak_ptr source) {
+ auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml"));
+ auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_MachineScope.yaml"));
+ matches.emplace_back(
+ ResultMatch(
+ TestCompositePackage::Make(
+ manifest,
+ TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } },
+ std::vector{ manifest2, manifest },
+ source
+ ),
+ PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller")));
+ });
+
const TestSourceResult TestInstaller_Exe_DifferentInstallerType(
"TestExeInstallerWithDifferentInstalledType"sv,
[](std::vector& matches, std::weak_ptr source) {
diff --git a/src/AppInstallerCLITests/WorkflowCommon.h b/src/AppInstallerCLITests/WorkflowCommon.h
index e4f6333780..3c4507d03e 100644
--- a/src/AppInstallerCLITests/WorkflowCommon.h
+++ b/src/AppInstallerCLITests/WorkflowCommon.h
@@ -33,6 +33,7 @@ namespace TestCommon
const extern TestSourceResult TestQuery_ReturnOne;
const extern TestSourceResult TestQuery_ReturnTwo;
const extern TestSourceResult TestInstaller_Exe;
+ const extern TestSourceResult TestInstaller_Exe_NotApplicable;
const extern TestSourceResult TestInstaller_Exe_Dependencies;
const extern TestSourceResult TestInstaller_Exe_DifferentInstallerType;
const extern TestSourceResult TestInstaller_Exe_ExpectedReturnCodes;