winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit c82ca612537f6dcecd210985ebbc58eda81b6e54
parent 4e4eee459c771db825ad18c9097e9497b00099bf
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Mon, 20 Apr 2020 16:56:23 -0700

Source management error handling (#87)


Diffstat:
Msrc/AppInstallerCLICore/Argument.cpp | 2++
Msrc/AppInstallerCLICore/Commands/SourceCommand.cpp | 36++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLICore/Commands/SourceCommand.h | 13+++++++++++++
Msrc/AppInstallerCLICore/ExecutionArgs.h | 1+
Msrc/AppInstallerCLICore/ExecutionReporter.h | 1+
Msrc/AppInstallerCLICore/Workflows/SourceFlow.cpp | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLICore/Workflows/SourceFlow.h | 24++++++++++++++++++++++++
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 11++++++++++-
Msrc/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h | 8++++++++
Msrc/AppInstallerRepositoryCore/RepositorySource.cpp | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
10 files changed, 199 insertions(+), 5 deletions(-)

diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp @@ -74,6 +74,8 @@ namespace AppInstaller::CLI return Argument{ "rainbow", None, Args::Type::RainbowStyle, LOCME("Progress display a rainbow of colors"), ArgumentType::Flag, Visibility::Hidden }; case Args::Type::PlainStyle: return Argument{ "plain", None, Args::Type::PlainStyle, LOCME("Progress display as the default color"), ArgumentType::Flag, Visibility::Hidden }; + case Args::Type::Force: + return Argument{ "force", None, Args::Type::Force, LOCME("Execute the command without prompts"), ArgumentType::Flag }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCLICore/Commands/SourceCommand.cpp b/src/AppInstallerCLICore/Commands/SourceCommand.cpp @@ -17,6 +17,7 @@ namespace AppInstaller::CLI std::make_unique<SourceListCommand>(FullName()), std::make_unique<SourceUpdateCommand>(FullName()), std::make_unique<SourceRemoveCommand>(FullName()), + std::make_unique<SourceResetCommand>(FullName()), }); } @@ -133,4 +134,39 @@ namespace AppInstaller::CLI Workflow::GetSourceListWithFilter << Workflow::RemoveSources; } + + std::vector<Argument> SourceResetCommand::GetArguments() const + { + return { + Argument::ForType(Args::Type::SourceName), + Argument::ForType(Args::Type::Force), + }; + } + + std::string SourceResetCommand::ShortDescription() const + { + return LOCME("Reset sources"); + } + + std::string SourceResetCommand::GetLongDescription() const + { + return LOCME("This command drops existing sources, potentially leaving any local data behind. Without any argument, it will drop all sources and add the defaults. If a named source is provided, only that source will be dropped."); + } + + void SourceResetCommand::ExecuteInternal(Context& context) const + { + if (context.Args.Contains(Args::Type::SourceName)) + { + context << + Workflow::GetSourceListWithFilter << + Workflow::ResetSourceList; + } + else + { + context << + Workflow::QueryUserForSourceReset << + Workflow::ResetAllSources << + Workflow::AddDefaultSources; + } + } } diff --git a/src/AppInstallerCLICore/Commands/SourceCommand.h b/src/AppInstallerCLICore/Commands/SourceCommand.h @@ -69,4 +69,17 @@ namespace AppInstaller::CLI protected: virtual void ExecuteInternal(Execution::Context& context) const override; }; + + struct SourceResetCommand final : public Command + { + SourceResetCommand(std::string_view parent) : Command("reset", parent) {} + + virtual std::vector<Argument> GetArguments() const override; + + virtual std::string ShortDescription() const override; + virtual std::string GetLongDescription() const override; + + protected: + virtual void ExecuteInternal(Execution::Context& context) const override; + }; } diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h @@ -51,6 +51,7 @@ namespace AppInstaller::CLI::Execution ValidateManifest, // Other + Force, // Generic flag to enable a command to skip some check ListVersions, // Used in Show command to list all available versions of an app NoVT, // Disable VirtualTerminal outputs PlainStyle, // Makes progress display as plain diff --git a/src/AppInstallerCLICore/ExecutionReporter.h b/src/AppInstallerCLICore/ExecutionReporter.h @@ -80,6 +80,7 @@ namespace AppInstaller::CLI::Execution // Sets the visual style (mostly for progress currently) void SetStyle(VisualStyle style); + // Prompts the user, return true if they consented. bool PromptForBoolResponse(const std::string& msg, Level level = Level::Info); // Used to show indefinite progress. Currently an indefinite spinner is the form of diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "SourceFlow.h" #include "TableOutput.h" +#include "WorkflowBase.h" namespace AppInstaller::CLI::Workflow { @@ -170,4 +171,50 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << "Done." << std::endl; } } + + void QueryUserForSourceReset(Execution::Context& context) + { + if (!context.Args.Contains(Execution::Args::Type::Force)) + { + context << GetSourceListWithFilter; + const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>(); + + if (!sources.empty()) + { + context.Reporter.Info() << "The following sources will be reset:" << std::endl; + + context << ListSources; + + if (!context.Reporter.PromptForBoolResponse("Do you wish to continue?")) + { + AICLI_TERMINATE_CONTEXT(E_ABORT); + } + } + } + } + + void ResetSourceList(Execution::Context& context) + { + const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>(); + + for (const auto& source : sources) + { + context.Reporter.Info() << "Reseting source: " << source.Name << " ..."; + Repository::DropSource(source.Name); + context.Reporter.Info() << " Done." << std::endl; + } + } + + void ResetAllSources(Execution::Context& context) + { + context.Reporter.Info() << "Reseting all sources ..."; + Repository::DropSource({}); + context.Reporter.Info() << " Done." << std::endl; + } + + void AddDefaultSources(Execution::Context& context) + { + context.Reporter.Info() << "Adding default sources ..." << std::endl; + context.Reporter.ExecuteWithProgress(Repository::AddDefaultSources); + } } diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.h b/src/AppInstallerCLICore/Workflows/SourceFlow.h @@ -46,4 +46,28 @@ namespace AppInstaller::CLI::Workflow // Inputs: SourceList // Outputs: None void RemoveSources(Execution::Context& context); + + // Removes the sources in SourceList. + // Required Args: None + // Inputs: None + // Outputs: None + void QueryUserForSourceReset(Execution::Context& context); + + // Removes the sources in SourceList. + // Required Args: None + // Inputs: SourceList + // Outputs: None + void ResetSourceList(Execution::Context& context); + + // Removes the sources in SourceList. + // Required Args: None + // Inputs: None + // Outputs: None + void ResetAllSources(Execution::Context& context); + + // Removes the sources in SourceList. + // Required Args: None + // Inputs: None + // Outputs: None + void AddDefaultSources(Execution::Context& context); } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -64,7 +64,16 @@ namespace AppInstaller::CLI::Workflow sourceName = context.Args.GetArg(Execution::Args::Type::Source); } - std::shared_ptr<Repository::ISource> source = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenSource, sourceName, std::placeholders::_1), true); + std::shared_ptr<Repository::ISource> source; + try + { + source = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenSource, sourceName, std::placeholders::_1), true); + } + catch (...) + { + context.Reporter.Error() << "Failed to open the source; try removing and re-adding it" << std::endl; + throw; + } if (!source) { diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h @@ -54,6 +54,9 @@ namespace AppInstaller::Repository // Adds a new source for the user. void AddSource(std::string name, std::string type, std::string arg, IProgressCallback& progress); + // Adds the default sources. + void AddDefaultSources(IProgressCallback& progress); + // Opens an existing source. // Passing an empty string as the name of the source will return a source that aggregates all others. std::shared_ptr<ISource> OpenSource(std::string_view name, IProgressCallback& progress); @@ -65,4 +68,9 @@ namespace AppInstaller::Repository // Removes an existing source. // Return value indicates whether the named source was found. bool RemoveSource(std::string_view name, IProgressCallback& progress); + + // Drops an existing source, with no attempt to clean up its data. + // Return value indicates whether the named source was found. + // Passing an empty string drops all sources. + bool DropSource(std::string_view name); } diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -102,14 +102,14 @@ namespace AppInstaller::Repository return true; } - // Gets the source details from a particular setting. - std::vector<SourceDetails> GetSourcesFromSetting(std::string_view settingName) + // Gets the source details from a particular setting, or an empty optional if no setting exists. + std::optional<std::vector<SourceDetails>> TryGetSourcesFromSetting(std::string_view settingName) { auto sourcesStream = Runtime::GetSettingStream(settingName); if (!sourcesStream) { - // TODO: Handle first run scenario and configure default source(s). - // Note that this case is different than the one in which all sources have been removed. + // Handle first run scenario and configure default source(s). + // Note that this case is different than the one in which all sources have been removed. return {}; } else @@ -120,6 +120,22 @@ namespace AppInstaller::Repository } } + // Gets the source details from a particular setting. + std::vector<SourceDetails> GetSourcesFromSetting(std::string_view settingName) + { + return TryGetSourcesFromSetting(settingName).value_or(std::vector<SourceDetails>{}); + } + + // If there is no setting value at all, adds the default sources. + void AddDefaultSourcesIfNeeded(IProgressCallback& progress) + { + auto sourcesStream = Runtime::GetSettingStream(s_RepositorySettings_UserSources); + if (!sourcesStream) + { + AddDefaultSources(progress); + } + } + // Make up for the lack of string_view support in YAML CPP. YAML::Emitter& operator<<(YAML::Emitter& out, std::string_view sv) { @@ -299,8 +315,16 @@ namespace AppInstaller::Repository SetSourcesToSetting(s_RepositorySettings_UserSources, currentSources); } + void AddDefaultSources(IProgressCallback& progress) + { + UNREFERENCED_PARAMETER(progress); + // TODO: Create list of default sources + } + std::shared_ptr<ISource> OpenSource(std::string_view name, IProgressCallback& progress) { + AddDefaultSourcesIfNeeded(progress); + std::vector<SourceDetails> currentSources = GetSources(); if (name.empty()) @@ -385,6 +409,35 @@ namespace AppInstaller::Repository } } + bool DropSource(std::string_view name) + { + if (name.empty()) + { + Runtime::RemoveSetting(s_RepositorySettings_UserSources); + return true; + } + else + { + std::vector<SourceDetails> currentSources = GetSourcesFromSetting(s_RepositorySettings_UserSources); + auto itr = FindSourceByName(currentSources, name); + + if (itr == currentSources.end()) + { + AICLI_LOG(Repo, Info, << "Named source to be dropped, but not found: " << name); + return false; + } + else + { + AICLI_LOG(Repo, Info, << "Named source to be dropped, found: " << itr->Name); + + currentSources.erase(itr); + SetSourcesToSetting(s_RepositorySettings_UserSources, currentSources); + + return true; + } + } + } + std::string SearchRequest::ToString() const { std::ostringstream result;