winget-cli

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

commit 4e4eee459c771db825ad18c9097e9497b00099bf
parent 217e274cc55153f5afc3485474544d18387dcfe8
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Mon, 20 Apr 2020 16:21:24 -0700

Various UI flow changes (#86)


Diffstat:
Msrc/AppInstallerCLICore/Argument.cpp | 2+-
Msrc/AppInstallerCLICore/Commands/InstallCommand.cpp | 1+
Msrc/AppInstallerCLICore/Commands/ShowCommand.cpp | 2++
Msrc/AppInstallerCLICore/ExecutionContext.cpp | 2+-
Msrc/AppInstallerCLICore/ExecutionReporter.cpp | 3+++
Msrc/AppInstallerCLICore/ExecutionReporter.h | 3+++
Msrc/AppInstallerCLICore/TableOutput.h | 9+++++++++
Msrc/AppInstallerCLICore/VTSupport.cpp | 4++++
Msrc/AppInstallerCLICore/VTSupport.h | 4++++
Msrc/AppInstallerCLICore/Workflows/InstallFlow.cpp | 13+++++++++++--
Msrc/AppInstallerCLICore/Workflows/InstallFlow.h | 6++++++
Msrc/AppInstallerCLICore/Workflows/ShowFlow.cpp | 29+++++++----------------------
Msrc/AppInstallerCLICore/Workflows/SourceFlow.cpp | 8+++++---
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 22+++++++++++++++++++++-
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.h | 12++++++++++++
Msrc/AppInstallerCLITests/WorkFlow.cpp | 8++++----
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]")