diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 6299beb909..ffc41ed56e 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -406,6 +406,7 @@ namespace AppInstaller::CLI args.push_back(ForType(Args::Type::RainbowStyle)); args.push_back(ForType(Args::Type::RetroStyle)); args.push_back(ForType(Args::Type::VerboseLogs)); + args.push_back(ForType(Args::Type::IgnoreWarnings)); args.emplace_back(Args::Type::DisableInteractivity, Resource::String::DisableInteractivityArgumentDescription, ArgumentType::Flag, false); args.push_back(ForType(Args::Type::Proxy)); args.push_back(ForType(Args::Type::NoProxy)); diff --git a/src/AppInstallerCLICore/ChannelStreams.h b/src/AppInstallerCLICore/ChannelStreams.h index 5fa7d77651..9c14550699 100644 --- a/src/AppInstallerCLICore/ChannelStreams.h +++ b/src/AppInstallerCLICore/ChannelStreams.h @@ -94,6 +94,10 @@ namespace AppInstaller::CLI::Execution OutputStream& operator<<(const VirtualTerminal::ConstructedSequence& sequence); OutputStream& operator<<(const std::filesystem::path& path); + inline bool IsEnabled() { + return m_enabled; + } + private: // Applies the format for the stream. void ApplyFormat(); diff --git a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp index afef0c25ee..b569fb9239 100644 --- a/src/AppInstallerCLICore/Commands/ValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ValidateCommand.cpp @@ -16,7 +16,6 @@ namespace AppInstaller::CLI { return { Argument::ForType(Execution::Args::Type::ValidateManifest), - Argument::ForType(Execution::Args::Type::IgnoreWarnings), }; } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index d8ecb88b1c..12feadf263 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -356,6 +356,12 @@ namespace AppInstaller::CLI::Execution Logging::Log().SetLevel(Logging::Level::Verbose); } + // Disable warnings if requested + if (Args.Contains(Args::Type::IgnoreWarnings)) + { + Reporter.SetLevelMask(Reporter::Level::Warning, false); + } + // Set proxy if (Args.Contains(Args::Type::Proxy)) { diff --git a/src/AppInstallerCLICore/ExecutionReporter.cpp b/src/AppInstallerCLICore/ExecutionReporter.cpp index 5350a87300..85de8e314b 100644 --- a/src/AppInstallerCLICore/ExecutionReporter.cpp +++ b/src/AppInstallerCLICore/ExecutionReporter.cpp @@ -54,6 +54,12 @@ namespace AppInstaller::CLI::Execution OutputStream Reporter::GetOutputStream(Level level) { + // If the level is not enabled, return a default stream which is disabled + if (WI_AreAllFlagsClear(m_enabledLevels, level)) + { + return OutputStream(*m_out, false, false); + } + OutputStream result = GetBasicOutputStream(); switch (level) @@ -111,15 +117,21 @@ namespace AppInstaller::CLI::Execution } } - bool Reporter::PromptForBoolResponse(Resource::LocString message, Level level) + bool Reporter::PromptForBoolResponse(Resource::LocString message, Level level, bool resultIfDisabled) { + auto out = GetOutputStream(level); + + if (!out.IsEnabled()) + { + return resultIfDisabled; + } + const std::vector options { BoolPromptOption{ Resource::String::PromptOptionYes, 'Y', true }, BoolPromptOption{ Resource::String::PromptOptionNo, 'N', false }, }; - auto out = GetOutputStream(level); out << message << std::endl; // Try prompting until we get a recognized option @@ -164,14 +176,24 @@ namespace AppInstaller::CLI::Execution void Reporter::PromptForEnter(Level level) { auto out = GetOutputStream(level); + if (!out.IsEnabled()) + { + return; + } + out << std::endl << Resource::String::PressEnterToContinue << std::endl; m_in.get(); } - std::filesystem::path Reporter::PromptForPath(Resource::LocString message, Level level) + std::filesystem::path Reporter::PromptForPath(Resource::LocString message, Level level, std::filesystem::path resultIfDisabled) { auto out = GetOutputStream(level); + if (!out.IsEnabled()) + { + return resultIfDisabled; + } + // Try prompting until we get a valid answer for (;;) { @@ -319,4 +341,16 @@ namespace AppInstaller::CLI::Execution } m_out->RestoreDefault(); } + + void Reporter::SetLevelMask(Level reporterLevel, bool setEnabled) { + + if (setEnabled) + { + WI_SetAllFlags(m_enabledLevels, reporterLevel); + } + else + { + WI_ClearAllFlags(m_enabledLevels, reporterLevel); + } + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/ExecutionReporter.h b/src/AppInstallerCLICore/ExecutionReporter.h index a7909d29c9..84e7aa6528 100644 --- a/src/AppInstallerCLICore/ExecutionReporter.h +++ b/src/AppInstallerCLICore/ExecutionReporter.h @@ -49,12 +49,14 @@ namespace AppInstaller::CLI::Execution }; // The level for the Output channel. - enum class Level + enum class Level : uint32_t { - Verbose, - Info, - Warning, - Error, + None = 0x0, + Verbose = 0x1, + Info = 0x2, + Warning = 0x4, + Error = 0x8, + All = Verbose | Info | Warning | Error, }; Reporter(std::ostream& outStream, std::istream& inStream); @@ -98,13 +100,13 @@ namespace AppInstaller::CLI::Execution void SetStyle(AppInstaller::Settings::VisualStyle style); // Prompts the user, return true if they consented. - bool PromptForBoolResponse(Resource::LocString message, Level level = Level::Info); + bool PromptForBoolResponse(Resource::LocString message, Level level = Level::Info, bool resultIfDisabled = false); // Prompts the user, continues when Enter is pressed void PromptForEnter(Level level = Level::Info); // Prompts the user for a path. - std::filesystem::path PromptForPath(Resource::LocString message, Level level = Level::Info); + std::filesystem::path PromptForPath(Resource::LocString message, Level level = Level::Info, std::filesystem::path resultIfDisabled = std::filesystem::path::path()); // Used to show indefinite progress. Currently an indefinite spinner is the form of // showing indefinite progress. @@ -165,9 +167,15 @@ namespace AppInstaller::CLI::Execution m_progressSink = sink; } + bool IsLevelEnabled(Level reporterLevel) + { + return WI_AreAllFlagsSet(m_enabledLevels, reporterLevel); + } + + void SetLevelMask(Level reporterLevel, bool setEnabled = true); + private: Reporter(std::shared_ptr outStream, std::istream& inStream); - // Gets a stream for output for internal use. OutputStream GetBasicOutputStream(); @@ -180,8 +188,13 @@ namespace AppInstaller::CLI::Execution wil::srwlock m_progressCallbackLock; std::atomic m_progressCallback; std::atomic m_progressSink; + + // Enable all levels by default + Level m_enabledLevels = Level::All; }; + DEFINE_ENUM_FLAG_OPERATORS(Reporter::Level); + // Indirection to enable change without tracking down every place extern const VirtualTerminal::Sequence& HelpCommandEmphasis; extern const VirtualTerminal::Sequence& HelpArgumentEmphasis; diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp index 77c1fbd9d1..279be1662a 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -1064,7 +1064,7 @@ namespace AppInstaller::CLI::Workflow } auto promptString = m_isApply ? Resource::String::ConfigurationWarningPromptApply : Resource::String::ConfigurationWarningPromptTest; - if (!context.Reporter.PromptForBoolResponse(promptString, Reporter::Level::Warning)) + if (!context.Reporter.PromptForBoolResponse(promptString, Reporter::Level::Warning, false)) { AICLI_TERMINATE_CONTEXT(WINGET_CONFIG_ERROR_WARNING_NOT_ACCEPTED); } diff --git a/src/AppInstallerCLICore/Workflows/PromptFlow.cpp b/src/AppInstallerCLICore/Workflows/PromptFlow.cpp index 45a8542116..8f7c21d362 100644 --- a/src/AppInstallerCLICore/Workflows/PromptFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/PromptFlow.cpp @@ -5,6 +5,7 @@ #include "ShowFlow.h" #include +using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility::literals; @@ -295,6 +296,12 @@ namespace AppInstaller::CLI::Workflow AICLI_LOG(CLI, Info, << "Prompting for install root."); m_installLocation = context.Reporter.PromptForPath(Resource::String::PromptForInstallRoot); + if (m_installLocation.empty()) + { + AICLI_LOG(CLI, Error, << "Install location is required but the provided path was empty."); + context.Reporter.Error() << Resource::String::InstallLocationNotProvided << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_LOCATION_REQUIRED); + } AICLI_LOG(CLI, Info, << "Proceeding with installation using install root: " << m_installLocation); } @@ -354,7 +361,7 @@ namespace AppInstaller::CLI::Workflow return; } - bool accepted = context.Reporter.PromptForBoolResponse(Resource::String::PromptToProceed, Execution::Reporter::Level::Warning); + bool accepted = context.Reporter.PromptForBoolResponse(Resource::String::PromptToProceed, Reporter::Level::Warning, true); if (accepted) { AICLI_LOG(CLI, Info, << "Proceeding with installation"); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 7f5838c20e..3f4c5df8e9 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -262,7 +262,7 @@ They can be configured through the settings file 'winget settings'. Filter results by id - Suppresses warning outputs. + Suppresses warning outputs This application is licensed to you by its owner.