commit 4e4eee459c771db825ad18c9097e9497b00099bf
parent 217e274cc55153f5afc3485474544d18387dcfe8
Author: JohnMcPMS <johnmcp@microsoft.com>
Date: Mon, 20 Apr 2020 16:21:24 -0700
Various UI flow changes (#86)
Diffstat:
16 files changed, 94 insertions(+), 34 deletions(-)
diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp
@@ -19,7 +19,7 @@ namespace AppInstaller::CLI
case Args::Type::Query:
return Argument{ "query", 'q', Args::Type::Query, LOCME("The query used to search for an app"), ArgumentType::Positional };
case Args::Type::Manifest:
- return Argument{ "manifest", 'm', Args::Type::Manifest, LOCME("The path to the manifest of the application to install"), ArgumentType::Standard, Visibility::Help };
+ return Argument{ "manifest", 'm', Args::Type::Manifest, LOCME("The path to the manifest of the application"), ArgumentType::Standard, Visibility::Help };
case Args::Type::Id:
return Argument{ "id", None, Args::Type::Id, LOCME("Filter results by id"), ArgumentType::Standard, Visibility::Help };
case Args::Type::Name:
diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp
@@ -53,6 +53,7 @@ namespace AppInstaller::CLI
Workflow::EnsureMinOSVersion <<
Workflow::SelectInstaller <<
Workflow::EnsureApplicableInstaller <<
+ Workflow::ShowInstallationDisclaimer <<
Workflow::DownloadInstaller <<
Workflow::VerifyInstallerHash <<
Workflow::ExecuteInstaller;
diff --git a/src/AppInstallerCLICore/Commands/ShowCommand.cpp b/src/AppInstallerCLICore/Commands/ShowCommand.cpp
@@ -42,6 +42,7 @@ namespace AppInstaller::CLI
{
context <<
Workflow::GetManifestFromArg <<
+ Workflow::ReportManifestIdentity <<
Workflow::ShowManifestVersion;
}
else
@@ -50,6 +51,7 @@ namespace AppInstaller::CLI
Workflow::OpenSource <<
Workflow::SearchSource <<
Workflow::EnsureOneMatchFromSearchResult <<
+ Workflow::ReportSearchResultIdentity <<
Workflow::ShowAppVersions;
}
}
diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp
@@ -82,7 +82,7 @@ namespace AppInstaller::CLI::Execution
}
else if (Args.Contains(Args::Type::PlainStyle))
{
- Reporter.SetStyle(VisualStyle::Rainbow);
+ Reporter.SetStyle(VisualStyle::Plain);
}
else if (Args.Contains(Args::Type::RainbowStyle))
{
diff --git a/src/AppInstallerCLICore/ExecutionReporter.cpp b/src/AppInstallerCLICore/ExecutionReporter.cpp
@@ -10,6 +10,9 @@ namespace AppInstaller::CLI::Execution
const Sequence& HelpCommandEmphasis = TextFormat::Foreground::BrightWhite;
const Sequence& HelpArgumentEmphasis = TextFormat::Foreground::BrightWhite;
+ const Sequence& NameEmphasis = TextFormat::Foreground::BrightCyan;
+ const Sequence& IdEmphasis = TextFormat::Foreground::BrightCyan;
+ const Sequence& UrlEmphasis = TextFormat::Foreground::BrightBlue;
Reporter::Reporter(std::ostream& outStream, std::istream& inStream) :
m_out(outStream),
diff --git a/src/AppInstallerCLICore/ExecutionReporter.h b/src/AppInstallerCLICore/ExecutionReporter.h
@@ -136,4 +136,7 @@ namespace AppInstaller::CLI::Execution
// Indirection to enable change without tracking down every place
extern const VirtualTerminal::Sequence& HelpCommandEmphasis;
extern const VirtualTerminal::Sequence& HelpArgumentEmphasis;
+ extern const VirtualTerminal::Sequence& NameEmphasis;
+ extern const VirtualTerminal::Sequence& IdEmphasis;
+ extern const VirtualTerminal::Sequence& UrlEmphasis;
}
diff --git a/src/AppInstallerCLICore/TableOutput.h b/src/AppInstallerCLICore/TableOutput.h
@@ -95,6 +95,15 @@ namespace AppInstaller::CLI::Execution
}
}
+ // If there are actually columns with data, then also bring in the minimum size
+ for (size_t i = 0; i < FieldCount; ++i)
+ {
+ if (m_columns[i].MaxLength)
+ {
+ m_columns[i].MaxLength = std::max(m_columns[i].MaxLength, m_columns[i].MinLength);
+ }
+ }
+
// Only output the extra space if:
// 1. Not the last field
m_columns[FieldCount - 1].SpaceAfter = false;
diff --git a/src/AppInstallerCLICore/VTSupport.cpp b/src/AppInstallerCLICore/VTSupport.cpp
@@ -119,7 +119,11 @@ namespace AppInstaller::CLI::VirtualTerminal
namespace Foreground
{
const Sequence BrightRed = AICLI_VT_TEXTFORMAT(91);
+ const Sequence BrightGreen = AICLI_VT_TEXTFORMAT(92);
const Sequence BrightYellow = AICLI_VT_TEXTFORMAT(93);
+ const Sequence BrightBlue = AICLI_VT_TEXTFORMAT(94);
+ const Sequence BrightMagenta = AICLI_VT_TEXTFORMAT(95);
+ const Sequence BrightCyan = AICLI_VT_TEXTFORMAT(96);
const Sequence BrightWhite = AICLI_VT_TEXTFORMAT(97);
ConstructedSequence Extended(const Color& color)
diff --git a/src/AppInstallerCLICore/VTSupport.h b/src/AppInstallerCLICore/VTSupport.h
@@ -99,7 +99,11 @@ namespace AppInstaller::CLI::VirtualTerminal
namespace Foreground
{
extern const Sequence BrightRed;
+ extern const Sequence BrightGreen;
extern const Sequence BrightYellow;
+ extern const Sequence BrightBlue;
+ extern const Sequence BrightMagenta;
+ extern const Sequence BrightCyan;
extern const Sequence BrightWhite;
ConstructedSequence Extended(const Color& color);
diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp
@@ -36,6 +36,13 @@ namespace AppInstaller::CLI::Workflow
}
}
+ void ShowInstallationDisclaimer(Execution::Context& context)
+ {
+ context.Reporter.Info() <<
+ "This application is licensed to you by its owner." << std::endl <<
+ "Microsoft is not responsible for, nor does it grant any licenses to, third-party packages." << std::endl;
+ }
+
void DownloadInstaller(Execution::Context& context)
{
const auto& installer = context.Get<Execution::Data::Installer>().value();
@@ -76,6 +83,8 @@ namespace AppInstaller::CLI::Workflow
AICLI_LOG(CLI, Info, << "Generated temp download path: " << tempInstallerPath);
+ context.Reporter.Info() << "Downloading " << Execution::UrlEmphasis << installer.Url << std::endl;
+
auto hash = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download,
installer.Url,
tempInstallerPath,
@@ -141,14 +150,14 @@ namespace AppInstaller::CLI::Workflow
if (!context.Reporter.PromptForBoolResponse("Installer hash verification failed. Continue?", Execution::Reporter::Level::Warning))
{
- context.Reporter.Error() << "Canceled. Installer hash mismatch." << std::endl;
+ context.Reporter.Error() << "Canceled; Installer hash mismatch" << std::endl;
AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH);
}
}
else
{
AICLI_LOG(CLI, Info, << "Installer hash verified");
- context.Reporter.Info() << "Successfully verified installer hash." << std::endl;
+ context.Reporter.Info() << "Successfully verified installer hash" << std::endl;
}
}
diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h
@@ -23,6 +23,12 @@ namespace AppInstaller::CLI::Workflow
// Outputs: None
void EnsureApplicableInstaller(Execution::Context& context);
+ // Shows the installation disclaimer.
+ // Required Args: None
+ // Inputs: None
+ // Outputs: None
+ void ShowInstallationDisclaimer(Execution::Context& context);
+
// Composite flow that chooses what to do based on the installer type.
// Required Args: None
// Inputs: Manifest, Installer
diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp
@@ -4,26 +4,12 @@
#include "pch.h"
#include "ShowFlow.h"
#include "ManifestComparator.h"
+#include "TableOutput.h"
using namespace AppInstaller::Repository;
namespace AppInstaller::CLI::Workflow
{
- namespace
- {
- void OutputVersionAndChannel(Execution::Context& context, std::string_view version, std::string_view channel)
- {
- auto out = context.Reporter.Info();
-
- out << " " << version;
- if (!channel.empty())
- {
- out << '[' << channel << ']';
- }
- out << std::endl;
- }
- }
-
void ShowManifestInfo(Execution::Context& context)
{
const auto& manifest = context.Get<Execution::Data::Manifest>();
@@ -33,8 +19,6 @@ namespace AppInstaller::CLI::Workflow
auto selectedLocalization = manifestComparator.GetPreferredLocalization(manifest);
// TODO: Come up with a prettier format
- context.Reporter.Info() << "Id: " + manifest.Id << std::endl;
- context.Reporter.Info() << "Name: " + manifest.Name << std::endl;
context.Reporter.Info() << "Version: " + manifest.Version << std::endl;
context.Reporter.Info() << "Author: " + manifest.Author << std::endl;
context.Reporter.Info() << "AppMoniker: " + manifest.AppMoniker << std::endl;
@@ -60,19 +44,20 @@ namespace AppInstaller::CLI::Workflow
{
const auto& manifest = context.Get<Execution::Data::Manifest>();
- context.Reporter.Info() << manifest.Id << ", " << manifest.Name << std::endl;
- OutputVersionAndChannel(context, manifest.Version, manifest.Channel);
+ Execution::TableOutput<2> table(context.Reporter, { "Version", "Channel" });
+ table.OutputLine({ manifest.Version, manifest.Channel });
+ table.Complete();
}
void ShowAppVersions(Execution::Context& context)
{
auto app = context.Get<Execution::Data::SearchResult>().Matches.at(0).Application.get();
- context.Reporter.Info() << app->GetId() << ", " << app->GetName() << std::endl;
-
+ Execution::TableOutput<2> table(context.Reporter, { "Version", "Channel" });
for (auto& version : app->GetVersions())
{
- OutputVersionAndChannel(context, version.GetVersion().ToString(), version.GetChannel().ToString());
+ table.OutputLine({ version.GetVersion().ToString(), version.GetChannel().ToString() });
}
+ table.Complete();
}
}
\ No newline at end of file
diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp
@@ -3,6 +3,7 @@
#pragma once
#include "pch.h"
#include "SourceFlow.h"
+#include "TableOutput.h"
namespace AppInstaller::CLI::Workflow
{
@@ -121,18 +122,19 @@ namespace AppInstaller::CLI::Workflow
}
else
{
- context.Reporter.Info() << "Current sources:" << std::endl;
if (sources.empty())
{
- context.Reporter.Info() << " <none>" << std::endl;
+ context.Reporter.Info() << "There are no sources configured." << std::endl;
}
else
{
+ Execution::TableOutput<2> table(context.Reporter, { "Name", "Arg" });
for (const auto& source : sources)
{
- context.Reporter.Info() << " " << source.Name << " -> " << source.Arg << std::endl;
+ table.OutputLine({ source.Name, source.Arg });
}
+ table.Complete();
}
}
}
diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp
@@ -27,6 +27,11 @@ namespace AppInstaller::CLI::Workflow
return {};
}
}
+
+ void ReportIdentity(Execution::Context& context, std::string_view name, std::string_view id)
+ {
+ context.Reporter.Info() << "Found " << Execution::NameEmphasis << name << " [" << Execution::IdEmphasis << id << ']' << std::endl;
+ }
}
bool WorkflowTask::operator==(const WorkflowTask& other) const
@@ -262,11 +267,25 @@ namespace AppInstaller::CLI::Workflow
};
}
+ void ReportSearchResultIdentity(Execution::Context& context)
+ {
+ auto app = context.Get<Execution::Data::SearchResult>().Matches.at(0).Application.get();
+ ReportIdentity(context, app->GetName(), app->GetId());
+ }
+
+ void ReportManifestIdentity(Execution::Context& context)
+ {
+ const auto& manifest = context.Get<Execution::Data::Manifest>();
+ ReportIdentity(context, manifest.Name, manifest.Id);
+ }
+
void GetManifest(Execution::Context& context)
{
if (context.Args.Contains(Execution::Args::Type::Manifest))
{
- context << GetManifestFromArg;
+ context <<
+ GetManifestFromArg <<
+ ReportManifestIdentity;
}
else
{
@@ -274,6 +293,7 @@ namespace AppInstaller::CLI::Workflow
OpenSource <<
SearchSource <<
EnsureOneMatchFromSearchResult <<
+ ReportSearchResultIdentity <<
GetManifestFromSearchResult;
}
}
diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h
@@ -98,6 +98,18 @@ namespace AppInstaller::CLI::Workflow
// Outputs: Manifest
void GetManifestFromArg(Execution::Context& context);
+ // Reports the search result's identity.
+ // Required Args: None
+ // Inputs: SearchResult (only 1)
+ // Outputs: None
+ void ReportSearchResultIdentity(Execution::Context& context);
+
+ // Reports the manifest's identity.
+ // Required Args: None
+ // Inputs: Manifest
+ // Outputs: None
+ void ReportManifestIdentity(Execution::Context& context);
+
// Composite flow that produces a manifest; either from one given on the command line or by searching.
// Required Args: None
// Inputs: None
diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp
@@ -461,10 +461,10 @@ TEST_CASE("InstallFlow_SearchAndShowAppInfo", "[ShowFlow]")
INFO(showOutput.str());
// Verify AppInfo is printed
- REQUIRE(showOutput.str().find("Id: AppInstallerCliTest.TestInstaller") != std::string::npos);
- REQUIRE(showOutput.str().find("Name: AppInstaller Test Installer") != std::string::npos);
- REQUIRE(showOutput.str().find("Version: 1.0.0.0") != std::string::npos);
- REQUIRE(showOutput.str().find(" Download Url: https://ThisIsNotUsed") != std::string::npos);
+ REQUIRE(showOutput.str().find("AppInstallerCliTest.TestInstaller") != std::string::npos);
+ REQUIRE(showOutput.str().find("AppInstaller Test Installer") != std::string::npos);
+ REQUIRE(showOutput.str().find("1.0.0.0") != std::string::npos);
+ REQUIRE(showOutput.str().find("https://ThisIsNotUsed") != std::string::npos);
}
TEST_CASE("InstallFlow_SearchAndShowAppVersion", "[ShowFlow]")