From 911a36fc338058eda812dd0d29fbf07933f9b3cb Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Sat, 21 Mar 2026 04:53:41 -0500 Subject: [PATCH] fix(install): defer success message until post-install checks - Add DeferInstallSuccessMessage, PendingDeferredInstallSuccess, and SuppressDeferredInstallSuccess context flags. - InstallPackageInstaller defers reporting until after ReportARPChanges, RecordInstall, and DisplayInstallationNotes. - ReportInstallerResult, MsixInstall, and MS Store handlers honor defer; MSIX registration-deferred path suppresses generic success. - ReportDeferredInstallSuccess avoids spurious success when the installer did not complete (e.g. lock cancellation). Made-with: Cursor --- src/AppInstallerCLICore/ExecutionContext.h | 6 +++ .../Workflows/InstallFlow.cpp | 52 ++++++++++++++++++- .../Workflows/InstallFlow.h | 3 ++ .../Workflows/MSStoreInstallerHandler.cpp | 28 ++++++++-- 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 6ad8b8e217..c3cf920e18 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -76,6 +76,12 @@ namespace AppInstaller::CLI::Execution RebootRequired = 0x400, RegisterResume = 0x800, InstallerExecutionUseRepair = 0x1000, + // InstallPackageInstaller: suppress immediate success until post-install checks complete. + DeferInstallSuccessMessage = 0x2000, + // MSIX registration deferred: do not emit generic success after checks (warning already shown). + SuppressDeferredInstallSuccess = 0x4000, + // Set when an install would have reported success but output was deferred. + PendingDeferredInstallSuccess = 0x8000, }; DEFINE_ENUM_FLAG_OPERATORS(ContextFlag); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index f702629c20..cb8f4bba00 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -255,6 +255,14 @@ namespace AppInstaller::CLI::Workflow if (registrationDeferred) { context.Reporter.Warn() << Resource::String::InstallFlowRegistrationDeferred << std::endl; + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DeferInstallSuccessMessage)) + { + context.SetFlags(Execution::ContextFlag::SuppressDeferredInstallSuccess); + } + } + else if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DeferInstallSuccessMessage)) + { + context.SetFlags(Execution::ContextFlag::PendingDeferredInstallSuccess); } else { @@ -575,7 +583,11 @@ namespace AppInstaller::CLI::Workflow } else { - if (isRepair) + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DeferInstallSuccessMessage)) + { + context.SetFlags(Execution::ContextFlag::PendingDeferredInstallSuccess); + } + else if (isRepair) { context.Reporter.Info() << Resource::String::RepairFlowRepairSuccess << std::endl; } @@ -586,6 +598,40 @@ namespace AppInstaller::CLI::Workflow } } + void ReportDeferredInstallSuccess(Execution::Context& context) + { + if (!WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DeferInstallSuccessMessage)) + { + return; + } + context.ClearFlags(Execution::ContextFlag::DeferInstallSuccessMessage); + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::SuppressDeferredInstallSuccess)) + { + context.ClearFlags(Execution::ContextFlag::SuppressDeferredInstallSuccess); + context.ClearFlags(Execution::ContextFlag::PendingDeferredInstallSuccess); + return; + } + if (context.IsTerminated()) + { + context.ClearFlags(Execution::ContextFlag::PendingDeferredInstallSuccess); + return; + } + if (!WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::PendingDeferredInstallSuccess)) + { + return; + } + context.ClearFlags(Execution::ContextFlag::PendingDeferredInstallSuccess); + const bool isRepair = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseRepair); + if (isRepair) + { + context.Reporter.Info() << Resource::String::RepairFlowRepairSuccess << std::endl; + } + else + { + context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + } + } + void ReportIdentityAndInstallationDisclaimer(Execution::Context& context) { context << @@ -595,6 +641,9 @@ namespace AppInstaller::CLI::Workflow void InstallPackageInstaller(Execution::Context& context) { + context.ClearFlags( + Execution::ContextFlag::PendingDeferredInstallSuccess | Execution::ContextFlag::SuppressDeferredInstallSuccess); + context.SetFlags(Execution::ContextFlag::DeferInstallSuccessMessage); context << Workflow::ReportExecutionStage(ExecutionStage::PreExecution) << Workflow::SnapshotARPEntries << @@ -606,6 +655,7 @@ namespace AppInstaller::CLI::Workflow Workflow::ForceInstalledCacheUpdate << Workflow::RemoveInstaller << Workflow::DisplayInstallationNotes; + ReportDeferredInstallSuccess(context); } void InstallDependencies(Execution::Context& context) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 2f2c056ab8..0e31eb3cd2 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -147,6 +147,9 @@ namespace AppInstaller::CLI::Workflow // Inputs: InstallerPath, Manifest, Installer, PackageVersion, InstalledPackageVersion? // Outputs: None void InstallPackageInstaller(Execution::Context& context); + + // Emits deferred install/repair success after post-install steps (ARP verification, etc.). + void ReportDeferredInstallSuccess(Execution::Context& context); // Installs the dependencies for a specific package. CreateDependencySubContexts should have been called before this task. // Required Args: None diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp index cfbb47fa9a..d10801df0d 100644 --- a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "MSStoreInstallerHandler.h" #include "WorkflowBase.h" +#include "ExecutionContext.h" #include #include #include @@ -159,7 +160,14 @@ namespace AppInstaller::CLI::Workflow if (SUCCEEDED(hr)) { - context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DeferInstallSuccessMessage)) + { + context.SetFlags(Execution::ContextFlag::PendingDeferredInstallSuccess); + } + else + { + context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + } } else { @@ -200,7 +208,14 @@ namespace AppInstaller::CLI::Workflow if (SUCCEEDED(hr)) { - context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DeferInstallSuccessMessage)) + { + context.SetFlags(Execution::ContextFlag::PendingDeferredInstallSuccess); + } + else + { + context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + } } else { @@ -241,7 +256,14 @@ namespace AppInstaller::CLI::Workflow if (SUCCEEDED(hr)) { - context.Reporter.Info() << Resource::String::RepairFlowRepairSuccess << std::endl; + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DeferInstallSuccessMessage)) + { + context.SetFlags(Execution::ContextFlag::PendingDeferredInstallSuccess); + } + else + { + context.Reporter.Info() << Resource::String::RepairFlowRepairSuccess << std::endl; + } } else {