commit c82ca612537f6dcecd210985ebbc58eda81b6e54
parent 4e4eee459c771db825ad18c9097e9497b00099bf
Author: JohnMcPMS <johnmcp@microsoft.com>
Date: Mon, 20 Apr 2020 16:56:23 -0700
Source management error handling (#87)
Diffstat:
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;