From 28cfa436e19eacd1cc5cc31f1600f248730d0d87 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 18 Oct 2023 16:37:34 -0700 Subject: [PATCH 01/20] comment typo --- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 2 +- .../Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs | 2 +- .../Commands/AsyncCommand.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 3803346ab8..a6011bb1cd 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2074,7 +2074,7 @@ Please specify one of them using the --source option to proceed. Specifies the location on the local computer to store modules. Default %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules - {Locked="allusers","currentuser","default","%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} + {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} `--module-path` value must be `currentuser`, `allusers`, `default` or an absolute path. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs index 62df3098a3..179543abbd 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs @@ -126,7 +126,7 @@ private static IntegrityCategory GetReason(PSCmdlet psCmdlet) } } - // Not under %LOCALAPPDATA%\\Microsoft\\WindowsApps\PFM\ + // Not under %LOCALAPPDATA%\\Microsoft\\WindowsApps\PFN\ // Check OS version if (!IsSupportedOSVersion()) diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/AsyncCommand.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/AsyncCommand.cs index d7171c5e5b..0951941ea5 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/AsyncCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/AsyncCommand.cs @@ -20,10 +20,10 @@ namespace Microsoft.WinGet.Configuration.Engine.Commands /// /// This is the base class for any command that performs async operations. /// It supports running tasks in an MTA thread via RunOnMta. - /// If the thread is already running on an MTA it will executed it, otherwise + /// If the thread is already running on an MTA it will execute it, otherwise /// it will create a new MTA thread. /// - /// Wait must be used to synchronously wait con the task. + /// Wait must be used to synchronously wait on the task. /// public abstract class AsyncCommand { From b11cf33e4b203957b15ae207dad7dca3981e56cd Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 18 Oct 2023 22:13:45 -0700 Subject: [PATCH 02/20] load from https --- .../Commands/ConfigureCommand.cpp | 2 +- .../Commands/ConfigureShowCommand.cpp | 2 +- .../Commands/ConfigureTestCommand.cpp | 2 +- .../Commands/ConfigureValidateCommand.cpp | 2 +- src/AppInstallerCLICore/Resources.h | 1 + .../Workflows/ConfigurationFlow.cpp | 53 ++++++++++++++----- .../Workflows/WorkflowBase.cpp | 35 ++++++++++++ .../Workflows/WorkflowBase.h | 14 +++++ .../Shared/Strings/en-us/winget.resw | 4 ++ .../Public/AppInstallerDownloader.h | 1 + 10 files changed, 99 insertions(+), 17 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/ConfigureCommand.cpp b/src/AppInstallerCLICore/Commands/ConfigureCommand.cpp index ba5fff1857..3b8f6daed6 100644 --- a/src/AppInstallerCLICore/Commands/ConfigureCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ConfigureCommand.cpp @@ -71,7 +71,7 @@ namespace AppInstaller::CLI { context << VerifyIsFullPackage << - VerifyFile(Execution::Args::Type::ConfigurationFile) << + VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) << CreateConfigurationProcessor << OpenConfigurationSet << ShowConfigurationSet << diff --git a/src/AppInstallerCLICore/Commands/ConfigureShowCommand.cpp b/src/AppInstallerCLICore/Commands/ConfigureShowCommand.cpp index 284a7654e1..c575cc349a 100644 --- a/src/AppInstallerCLICore/Commands/ConfigureShowCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ConfigureShowCommand.cpp @@ -37,7 +37,7 @@ namespace AppInstaller::CLI void ConfigureShowCommand::ExecuteInternal(Execution::Context& context) const { context << - VerifyFile(Execution::Args::Type::ConfigurationFile) << + VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) << CreateConfigurationProcessor << OpenConfigurationSet << ShowConfigurationSet; diff --git a/src/AppInstallerCLICore/Commands/ConfigureTestCommand.cpp b/src/AppInstallerCLICore/Commands/ConfigureTestCommand.cpp index 63e8d56428..bfc53f78a9 100644 --- a/src/AppInstallerCLICore/Commands/ConfigureTestCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ConfigureTestCommand.cpp @@ -38,7 +38,7 @@ namespace AppInstaller::CLI { context << VerifyIsFullPackage << - VerifyFile(Execution::Args::Type::ConfigurationFile) << + VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) << CreateConfigurationProcessor << OpenConfigurationSet << ShowConfigurationSet << diff --git a/src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp index 83b17e5d91..84368b0c4b 100644 --- a/src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp @@ -37,7 +37,7 @@ namespace AppInstaller::CLI { context << VerifyIsFullPackage << - VerifyFile(Execution::Args::Type::ConfigurationFile) << + VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) << CreateConfigurationProcessor << OpenConfigurationSet << ValidateConfigurationSetSemantics << diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 8beb0be175..2becd0cfc8 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -568,6 +568,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(UpgradeRequireExplicitCount); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionCount); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionExplanation); + WINGET_DEFINE_RESOURCE_STRINGID(UriSchemeNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(Usage); WINGET_DEFINE_RESOURCE_STRINGID(UserSettings); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandLongDescription); diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp index d693e86bd2..0e9f04d8ee 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -4,6 +4,7 @@ #include "ConfigurationFlow.h" #include "PromptFlow.h" #include "Public/ConfigurationSetProcessorFactoryRemoting.h" +#include #include #include #include @@ -769,12 +770,6 @@ namespace AppInstaller::CLI::Workflow bool m_isFirstProgress = true; }; - - std::filesystem::path GetConfigurationFilePath(Execution::Context& context) - { - std::filesystem::path argPath = Utility::ConvertToUTF16(context.Args.GetArg(Args::Type::ConfigurationFile)); - return std::filesystem::weakly_canonical(argPath); - } } void CreateConfigurationProcessor(Context& context) @@ -809,10 +804,32 @@ namespace AppInstaller::CLI::Workflow auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationReadingConfigFile()); - std::filesystem::path absolutePath = GetConfigurationFilePath(context); - + std::string argPath{ context.Args.GetArg(Args::Type::ConfigurationFile) }; + std::wstring argPathWide = Utility::ConvertToUTF16(argPath); + bool isRemote = Utility::IsUrlRemote(argPath); + std::filesystem::path absolutePath; Streams::IInputStream inputStream = nullptr; + + if (isRemote) { + std::ostringstream stringStream; + ProgressCallback emptyCallback; + Utility::DownloadToStream(argPath, stringStream, Utility::DownloadType::ConfigurationFile, emptyCallback); + + auto strContent = stringStream.str(); + std::vector byteContent{ strContent.begin(), strContent.end() }; + + Streams::InMemoryRandomAccessStream memoryStream; + Streams::DataWriter streamWriter{ memoryStream }; + streamWriter.WriteBytes(byteContent); + streamWriter.StoreAsync().get(); + streamWriter.DetachStream(); + memoryStream.Seek(0); + inputStream = memoryStream; + } + else + { + absolutePath = std::filesystem::weakly_canonical(std::filesystem::path{ argPathWide }); auto openAction = Streams::FileRandomAccessStream::OpenAsync(absolutePath.wstring(), FileAccessMode::Read); auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { openAction.Cancel(); }); inputStream = openAction.get(); @@ -829,7 +846,7 @@ namespace AppInstaller::CLI::Workflow if (FAILED_LOG(static_cast(openResult.ResultCode().value))) { - AICLI_LOG(Config, Error, << "Failed to open configuration set at " << absolutePath.u8string() << " with error 0x" << Logging::SetHRFormat << static_cast(openResult.ResultCode().value)); + AICLI_LOG(Config, Error, << "Failed to open configuration set at " << (isRemote ? argPath : absolutePath.u8string()) << " with error 0x" << Logging::SetHRFormat << static_cast(openResult.ResultCode().value)); switch (openResult.ResultCode()) { @@ -863,10 +880,20 @@ namespace AppInstaller::CLI::Workflow ConfigurationSet result = openResult.Set(); // Fill out the information about the set based on it coming from a file. - // TODO: Consider how to properly determine a good value for name and origin. - result.Name(absolutePath.filename().wstring()); - result.Origin(absolutePath.parent_path().wstring()); - result.Path(absolutePath.wstring()); + if (isRemote) + { + // TODO: Suggestions welcome on what values to pass in. + result.Name(argPathWide); + result.Origin(argPathWide); + result.Path(argPathWide); + } + else + { + // TODO: Consider how to properly determine a good value for name and origin. + result.Name(absolutePath.filename().wstring()); + result.Origin(absolutePath.parent_path().wstring()); + result.Path(absolutePath.wstring()); + } context.Get().Set(result); } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index cdd114a516..ef495cdb1a 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -16,6 +16,7 @@ using namespace AppInstaller::Utility::literals; using namespace AppInstaller::Pinning; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; +using namespace winrt::Windows::Foundation; namespace AppInstaller::CLI::Workflow { @@ -1093,6 +1094,40 @@ namespace AppInstaller::CLI::Workflow } } + void VerifyFileOrUri::operator()(Execution::Context& context) const + { + auto path = context.Args.GetArg(m_arg); + + // try uri first + Uri pathAsUri = nullptr; + try + { + pathAsUri = Uri{ Utility::ConvertToUTF16(path) }; + } + catch (...) {} + + if (pathAsUri && !pathAsUri.Suspicious()) + { + // SchemeName() always returns lower case + if (L"file" == pathAsUri.SchemeName()) + { + // Let VerifyFile do the work. + } + else if (L"https" != pathAsUri.SchemeName()) + { + // Currently only https supported. + return; + } + else + { + context.Reporter.Error() << Resource::String::UriSchemeNotSupported(Utility::LocIndView{ path }) << std::endl; + AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + } + + context << VerifyFile(m_arg); + } + void GetManifestFromArg(Execution::Context& context) { Logging::Telemetry().LogIsManifestLocal(true); diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index dbaaa911b6..31bbeebeaf 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -319,6 +319,20 @@ namespace AppInstaller::CLI::Workflow Execution::Args::Type m_arg; }; + // Ensures the local file exists and is not a directory. Or it's a Uri. Only https is supported at the moment. + // Required Args: the one given + // Inputs: None + // Outputs: None + struct VerifyFileOrUri : public WorkflowTask + { + VerifyFileOrUri(Execution::Args::Type arg) : WorkflowTask("VerifyFileOrUri"), m_arg(arg) {} + + void operator()(Execution::Context& context) const override; + + private: + Execution::Args::Type m_arg; + }; + // Opens the manifest file provided on the command line. // Required Args: Manifest // Inputs: None diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index a6011bb1cd..8f4cc8eac1 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2584,4 +2584,8 @@ Please specify one of them using the --source option to proceed. Unavailable + + Uri scheme not supported: {0} + {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. + \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h index aa202cbb7c..c99e10767c 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h @@ -31,6 +31,7 @@ namespace AppInstaller::Utility WinGetUtil, Installer, InstallerMetadataCollectionInput, + ConfigurationFile, }; // Extra metadata about a download for use by certain downloaders (Delivery Optimization for instance). From 08f9435e25bc97c73e2089fa9526f45354750c9a Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Sun, 22 Oct 2023 18:22:57 -0700 Subject: [PATCH 03/20] stage --- .../AppInstallerCLICore.vcxproj | 2 + .../Commands/ConfigureValidateCommand.cpp | 1 + ...igurationWingetDscModuleUnitValidation.cpp | 3 ++ ...nfigurationWingetDscModuleUnitValidation.h | 43 +++++++++++++++++++ .../Workflows/ConfigurationFlow.cpp | 31 +++++++++++++ .../Workflows/ConfigurationFlow.h | 6 +++ .../Workflows/WorkflowBase.cpp | 16 ++++--- .../Workflows/WorkflowBase.h | 6 ++- 8 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp create mode 100644 src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index f52f3ac068..15edaf3279 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -380,6 +380,7 @@ + @@ -436,6 +437,7 @@ + diff --git a/src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp b/src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp index 84368b0c4b..024b450e7a 100644 --- a/src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp @@ -42,6 +42,7 @@ namespace AppInstaller::CLI OpenConfigurationSet << ValidateConfigurationSetSemantics << ValidateConfigurationSetUnitProcessors << + ValidateConfigurationSetUnitContents << ValidateAllGoodMessage; } diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp new file mode 100644 index 0000000000..bfcc38f278 --- /dev/null +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -0,0 +1,3 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" \ No newline at end of file diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h new file mode 100644 index 0000000000..d542afca1b --- /dev/null +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include + +namespace winrt::Microsoft::Management::Configuration +{ + struct ConfigurationProcessor; + struct ConfigurationSet; +} + +namespace AppInstaller::CLI::Execution +{ + namespace details + { + struct ConfigurationContextData; + } + + struct ConfigurationContext + { + ConfigurationContext(); + ~ConfigurationContext(); + + ConfigurationContext(ConfigurationContext&) = delete; + ConfigurationContext& operator=(ConfigurationContext&) = delete; + + ConfigurationContext(ConfigurationContext&&); + ConfigurationContext& operator=(ConfigurationContext&&); + + winrt::Microsoft::Management::Configuration::ConfigurationProcessor& Processor(); + const winrt::Microsoft::Management::Configuration::ConfigurationProcessor& Processor() const; + void Processor(const winrt::Microsoft::Management::Configuration::ConfigurationProcessor& value); + void Processor(winrt::Microsoft::Management::Configuration::ConfigurationProcessor&& value); + + winrt::Microsoft::Management::Configuration::ConfigurationSet& Set(); + const winrt::Microsoft::Management::Configuration::ConfigurationSet& Set() const; + void Set(const winrt::Microsoft::Management::Configuration::ConfigurationSet& value); + void Set(winrt::Microsoft::Management::Configuration::ConfigurationSet&& value); + + private: + std::unique_ptr m_data; + }; +} diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp index 0e9f04d8ee..d9adb12ef9 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -1328,6 +1328,37 @@ namespace AppInstaller::CLI::Workflow } } + void ValidateConfigurationSetUnitContents(Execution::Context& context) + { + ConfigurationContext& configContext = context.Get(); + auto units = configContext.Set().Units(); + + bool foundIssues = false; + for (uint32_t i = 0; i < units.Size(); ++i) + { + const ConfigurationUnit& unit = units.GetAt(i); + auto details = unit.Details(); + auto metadata = unit.Metadata(); + auto settings = unit.Settings(); + foundIssues = details.IsLocal(); + for (const auto& [key, value] : metadata) + { + std::wcout << key.c_str() << std::endl; + } + for (const auto& [key, value] : settings) + { + std::wcout << key.c_str() << std::endl; + } + (settings); + } + + if (foundIssues) + { + // Indicate that it was not a total success + AICLI_TERMINATE_CONTEXT(S_FALSE); + } + } + void ValidateAllGoodMessage(Execution::Context& context) { context.Reporter.Info() << Resource::String::ConfigurationValidationFoundNoIssues << std::endl; diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.h b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.h index a5dcb44e76..83aebf36f9 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.h +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.h @@ -73,6 +73,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void ValidateConfigurationSetUnitProcessors(Execution::Context& context); + // Validates that specifc unit contents referenced by the set are valid/available/etc. + // Required Args: None + // Inputs: ConfigurationProcessor, ConfigurationSet + // Outputs: None + void ValidateConfigurationSetUnitContents(Execution::Context& context); + // Outputs the final message stating that no issues were found. // Required Args: None // Inputs: None diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index ef495cdb1a..804d92a63c 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1109,13 +1109,15 @@ namespace AppInstaller::CLI::Workflow if (pathAsUri && !pathAsUri.Suspicious()) { // SchemeName() always returns lower case - if (L"file" == pathAsUri.SchemeName()) + if (L"file" == pathAsUri.SchemeName() && !Utility::CaseInsensitiveStartsWith(path, "file:")) { - // Let VerifyFile do the work. + // Uri constructor is smart enough to parse an absolute local file path to file uri. + // In this case, we should continue with VerifyFile. + context << VerifyFile(m_arg); } - else if (L"https" != pathAsUri.SchemeName()) + else if (std::find(m_supportedSchemes.begin(), m_supportedSchemes.end(), pathAsUri.SchemeName()) != m_supportedSchemes.end()) { - // Currently only https supported. + // Scheme supported. return; } else @@ -1124,8 +1126,10 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } - - context << VerifyFile(m_arg); + else + { + context << VerifyFile(m_arg); + } } void GetManifestFromArg(Execution::Context& context) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index 31bbeebeaf..1049325fa4 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -319,18 +319,20 @@ namespace AppInstaller::CLI::Workflow Execution::Args::Type m_arg; }; - // Ensures the local file exists and is not a directory. Or it's a Uri. Only https is supported at the moment. + // Ensures the local file exists and is not a directory. Or it's a Uri. Default only https is supported at the moment. // Required Args: the one given // Inputs: None // Outputs: None struct VerifyFileOrUri : public WorkflowTask { - VerifyFileOrUri(Execution::Args::Type arg) : WorkflowTask("VerifyFileOrUri"), m_arg(arg) {} + VerifyFileOrUri(Execution::Args::Type arg, std::vector supportedSchemes = { L"https" }) : + WorkflowTask("VerifyFileOrUri"), m_arg(arg), m_supportedSchemes(std::move(supportedSchemes)) {} void operator()(Execution::Context& context) const override; private: Execution::Args::Type m_arg; + std::vector m_supportedSchemes; }; // Opens the manifest file provided on the command line. From a9d94eb90980d37d4f0cfd8d53bafabbff8cc6eb Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Mon, 23 Oct 2023 20:56:03 -0700 Subject: [PATCH 04/20] stage 2 --- ...igurationWingetDscModuleUnitValidation.cpp | 15 +++- ...nfigurationWingetDscModuleUnitValidation.h | 39 ++++------ .../Workflows/ConfigurationFlow.cpp | 75 +++++++++++++++---- 3 files changed, 89 insertions(+), 40 deletions(-) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index bfcc38f278..750e2f5fff 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -1,3 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "pch.h" \ No newline at end of file +#include "pch.h" +#include "ConfigurationWingetDscModuleUnitValidation.h" +#include "ExecutionContext.h" +#include + +using namespace winrt::Microsoft::Management::Configuration; + +namespace AppInstaller::CLI::Configuration +{ + bool WingetDscModuleUnitValidator::ValidateConfigurationSetUnit(Execution::Context& context, const ConfigurationUnit& unit) + { + + } +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h index d542afca1b..74a59f7c6c 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h @@ -1,43 +1,30 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include +#include +#include namespace winrt::Microsoft::Management::Configuration { - struct ConfigurationProcessor; - struct ConfigurationSet; + struct ConfigurationUnit; } namespace AppInstaller::CLI::Execution { - namespace details - { - struct ConfigurationContextData; - } - - struct ConfigurationContext - { - ConfigurationContext(); - ~ConfigurationContext(); - - ConfigurationContext(ConfigurationContext&) = delete; - ConfigurationContext& operator=(ConfigurationContext&) = delete; + struct Context; +} - ConfigurationContext(ConfigurationContext&&); - ConfigurationContext& operator=(ConfigurationContext&&); +namespace AppInstaller::CLI::Configuration +{ + using namespace std::string_view_literals; - winrt::Microsoft::Management::Configuration::ConfigurationProcessor& Processor(); - const winrt::Microsoft::Management::Configuration::ConfigurationProcessor& Processor() const; - void Processor(const winrt::Microsoft::Management::Configuration::ConfigurationProcessor& value); - void Processor(winrt::Microsoft::Management::Configuration::ConfigurationProcessor&& value); + struct WingetDscModuleUnitValidator + { + bool ValidateConfigurationSetUnit(AppInstaller::CLI::Execution::Context& context, const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit); - winrt::Microsoft::Management::Configuration::ConfigurationSet& Set(); - const winrt::Microsoft::Management::Configuration::ConfigurationSet& Set() const; - void Set(const winrt::Microsoft::Management::Configuration::ConfigurationSet& value); - void Set(winrt::Microsoft::Management::Configuration::ConfigurationSet&& value); + std::string_view ModuleName() { return "Microsoft.WinGet.DSC"sv; }; private: - std::unique_ptr m_data; + std::map dependenciesSourceAndUnitIdMap; }; } diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp index d9adb12ef9..c2afb0aaba 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -6,9 +6,11 @@ #include "Public/ConfigurationSetProcessorFactoryRemoting.h" #include #include +#include #include #include #include "ConfigurationCommon.h" +#include "ConfigurationWingetDscModuleUnitValidation.h" using namespace AppInstaller::CLI::Execution; using namespace winrt::Microsoft::Management::Configuration; @@ -770,6 +772,54 @@ namespace AppInstaller::CLI::Workflow bool m_isFirstProgress = true; }; + + std::string GetNormalizedIdentifier(const winrt::hstring& identifier) + { + return Utility::FoldCase(Utility::NormalizedString{ identifier }); + } + + // Get unit validation order. Make sure dependency units are before units depending on them. + std::vector GetConfigurationSetUnitValidationOrder(winrt::Windows::Foundation::Collections::IVectorView units) + { + // Create id to index map for easier processing. + std::map idToUnitIndex; + for (uint32_t i = 0; i < units.Size(); ++i) + { + auto id = GetNormalizedIdentifier(units.GetAt(i).Identifier()); + if (!id.empty()) + { + idToUnitIndex.emplace(std::move(id), i); + } + } + + // We do not need to worry about duplicate id, missing dependency or loops + // since dependency integrity is already validated in earlier semantic checks. + + std::vector validationOrder; + + std::function addUnitToValidationOrder = + [&](const ConfigurationUnit& unit, uint32_t index) + { + if (std::find(validationOrder.begin(), validationOrder.end(), index) == validationOrder.end()) + { + for (auto const& dependencyId : unit.Dependencies()) + { + auto dependencyIndex = idToUnitIndex.find(GetNormalizedIdentifier(dependencyId))->second; + addUnitToValidationOrder(units.GetAt(dependencyIndex), dependencyIndex); + } + validationOrder.emplace_back(index); + } + }; + + for (uint32_t i = 0; i < units.Size(); ++i) + { + addUnitToValidationOrder(units.GetAt(i), i); + } + + THROW_HR_IF(E_UNEXPECTED, units.Size() != validationOrder.size()); + + return validationOrder; + } } void CreateConfigurationProcessor(Context& context) @@ -1332,24 +1382,23 @@ namespace AppInstaller::CLI::Workflow { ConfigurationContext& configContext = context.Get(); auto units = configContext.Set().Units(); + auto validationOrder = GetConfigurationSetUnitValidationOrder(units.GetView()); + + Configuration::WingetDscModuleUnitValidator wingetUnitValidator; bool foundIssues = false; - for (uint32_t i = 0; i < units.Size(); ++i) + for (const auto index : validationOrder) { - const ConfigurationUnit& unit = units.GetAt(i); - auto details = unit.Details(); - auto metadata = unit.Metadata(); - auto settings = unit.Settings(); - foundIssues = details.IsLocal(); - for (const auto& [key, value] : metadata) + const ConfigurationUnit& unit = units.GetAt(index); + auto moduleName = Utility::ConvertToUTF8(unit.Details().ModuleName()); + if (Utility::CaseInsensitiveEquals(wingetUnitValidator.ModuleName(), moduleName)) { - std::wcout << key.c_str() << std::endl; - } - for (const auto& [key, value] : settings) - { - std::wcout << key.c_str() << std::endl; + auto result = wingetUnitValidator.ValidateConfigurationSetUnit(context, unit); + if (!result) + { + foundIssues = true; + } } - (settings); } if (foundIssues) From 5f0f03430632fb3a2589ad79dbb0fa6bd0a3f8c2 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Tue, 24 Oct 2023 14:35:04 -0700 Subject: [PATCH 05/20] comp --- ...igurationWingetDscModuleUnitValidation.cpp | 350 ++++++++++++++++++ ...nfigurationWingetDscModuleUnitValidation.h | 2 +- 2 files changed, 351 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index 750e2f5fff..3463c5dd1d 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -6,11 +6,361 @@ #include using namespace winrt::Microsoft::Management::Configuration; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; namespace AppInstaller::CLI::Configuration { + namespace + { + constexpr static std::string_view UnitType_WinGetSources = "WinGetSources"sv; + constexpr static std::string_view UnitType_WinGetPackage = "WinGetPackage"sv; + + constexpr static std::string_view WellKnownSourceName_WinGet = "winget"sv; + constexpr static std::string_view WellKnownSourceName_MSStore = "msstore"sv; + + struct WinGetSource + { + std::string Name; + std::string Type = "Microsoft.PreIndexed.Package"; + std::string Arg; + + bool Empty() + { + return Name.empty() && Arg.empty(); + } + }; + + std::string GetPropertyValueAsString(const winrt::Windows::Foundation::IInspectable& value) + { + IPropertyValue propertyValue = value.try_as(); + if (propertyValue && propertyValue.Type() == PropertyType::String) + { + return Utility::ConvertToUTF8(propertyValue.GetString()); + } + + return {}; + } + + bool GetPropertyValueAsBoolean(const winrt::Windows::Foundation::IInspectable& value, bool defaultIfFailed = false) + { + IPropertyValue propertyValue = value.try_as(); + if (propertyValue && propertyValue.Type() == PropertyType::Boolean) + { + return propertyValue.GetBoolean(); + } + + return defaultIfFailed; + } + + std::vector ParseWinGetSourcesFromSettings(const ValueSet& settings) + { + // Iterate through the value set as Powershell variables are case insensitive. + std::vector result; + for (auto const& settingsPair : settings) + { + auto settingsKey = Utility::ConvertToUTF8(settingsPair.Key()); + if (Utility::CaseInsensitiveEquals("sources", settingsKey)) + { + auto sources = settingsPair.Value().try_as(); + if (!sources) + { + return {}; + } + bool isArray = false; + for (auto const& sourcesPair : sources) + { + if (Utility::CaseInsensitiveEquals("treatAsArray", Utility::ConvertToUTF8(sourcesPair.Key()))) + { + isArray = true; + } + else + { + auto source = sourcesPair.Value().try_as(); + if (source) + { + WinGetSource wingetSource; + for (auto const& sourcePair : source) + { + auto sourceKey = Utility::ConvertToUTF8(sourcesPair.Key()); + if (Utility::CaseInsensitiveEquals("Name", sourceKey)) + { + wingetSource.Name = GetPropertyValueAsString(sourcesPair.Value()); + } + else if (Utility::CaseInsensitiveEquals("Type", sourceKey)) + { + wingetSource.Type = GetPropertyValueAsString(sourcesPair.Value()); + } + else if (Utility::CaseInsensitiveEquals("Arg", sourceKey)) + { + wingetSource.Arg = GetPropertyValueAsString(sourcesPair.Value()); + } + } + + if (!wingetSource.Empty()) + { + result.emplace_back(std::move(wingetSource)); + } + } + } + } + + if (!isArray) + { + return {}; + } + + break; + } + } + + return result; + } + + bool IsWellKnownSourceName(std::string_view sourceName) + { + return Utility::CaseInsensitiveEquals(WellKnownSourceName_WinGet, sourceName) || + Utility::CaseInsensitiveEquals(WellKnownSourceName_MSStore, sourceName); + } + + bool ValidateWellKnownSource(const WinGetSource& source) + { + static std::vector wellKnownSourceDetails = + { + Repository::Source{ Repository::WellKnownSource::WinGet }.GetDetails(), + Repository::Source{ Repository::WellKnownSource::MicrosoftStore }.GetDetails(), + }; + + for (auto const& wellKnownSource : wellKnownSourceDetails) + { + if (Utility::CaseInsensitiveEquals(wellKnownSource.Name, source.Name) && + Utility::CaseInsensitiveEquals(wellKnownSource.Arg, source.Arg) && + Utility::CaseInsensitiveEquals(wellKnownSource.Type, source.Type)) + { + return true; + } + } + + return false; + } + + struct WinGetPackage + { + std::string Id; + std::string Version; + std::string Source; + bool UseLatest = false; + + bool Empty() + { + return Id.empty() && Version.empty() && Source.empty(); + } + }; + + WinGetPackage ParseWinGetPackageFromSettings(const ValueSet& settings) + { + // Iterate through the value set as Powershell variables are case insensitive. + WinGetPackage result; + for (auto const& settingsPair : settings) + { + auto settingsKey = Utility::ConvertToUTF8(settingsPair.Key()); + if (Utility::CaseInsensitiveEquals("id", settingsKey)) + { + result.Id = GetPropertyValueAsString(settingsPair.Value()); + } + else if (Utility::CaseInsensitiveEquals("version", settingsKey)) + { + result.Version = GetPropertyValueAsString(settingsPair.Value()); + } + else if (Utility::CaseInsensitiveEquals("source", settingsKey)) + { + result.Source = GetPropertyValueAsString(settingsPair.Value()); + } + else if (Utility::CaseInsensitiveEquals("useLatest", settingsKey)) + { + result.UseLatest = GetPropertyValueAsBoolean(settingsPair.Value()); + } + } + + return result; + } + } + bool WingetDscModuleUnitValidator::ValidateConfigurationSetUnit(Execution::Context& context, const ConfigurationUnit& unit) { + bool foundIssues = false; + auto details = unit.Details(); + auto unitType = Utility::ConvertToUTF8(details.UnitType()); + auto unitIntent = unit.Intent(); + + if (Utility::CaseInsensitiveEquals(UnitType_WinGetSources, unitType)) + { + if (unitIntent == ConfigurationUnitIntent::Assert || unitIntent == ConfigurationUnitIntent::Apply) + { + auto sources = ParseWinGetSourcesFromSettings(unit.Settings()); + if (sources.size() == 0) + { + AICLI_LOG(Config, Warning, << "Failed to parse WinGetSources or empty content."); + foundIssues = true; + } + for (auto const& source : sources) + { + // Validate basic semantics. + if (source.Name.empty()) + { + AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Name"); + foundIssues = true; + } + if (source.Arg.empty()) + { + AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Arg"); + foundIssues = true; + } + + // Validate well known source or process 3rd party source. + if (IsWellKnownSourceName(source.Name)) + { + if (!ValidateWellKnownSource(source)) + { + AICLI_LOG(Config, Warning, << "WinGetSource " << source.Name << " conflicts with a well known source."); + foundIssues = true; + } + } + else + { + if (unitIntent == ConfigurationUnitIntent::Assert) + { + AICLI_LOG(Config, Warning, << "Asserting on 3rd party source: " << source.Name); + foundIssues = true; + } + else + { + // Add to dependency source map so it can be validated with later WinGetPackage source + m_dependenciesSourceAndUnitIdMap.emplace(Utility::FoldCase(std::string_view{ source.Name }), Utility::FoldCase(Utility::NormalizedString{ unit.Identifier() })); + } + } + } + } + } + else if (Utility::CaseInsensitiveEquals(UnitType_WinGetPackage, unitType)) + { + if (unitIntent == ConfigurationUnitIntent::Assert || unitIntent == ConfigurationUnitIntent::Apply) + { + auto package = ParseWinGetPackageFromSettings(unit.Settings()); + // Validate basic semantics. + if (package.Id.empty()) + { + AICLI_LOG(Config, Error, << "WinGetPackage unit missing required arg: Id"); + foundIssues = true; + } + if (package.Source.empty()) + { + AICLI_LOG(Config, Warning, << "WinGetPackage unit missing recommeended arg: Source"); + foundIssues = true; + } + if (package.UseLatest && !package.Version.empty()) + { + AICLI_LOG(Config, Error, << "WinGetPackage unit both UseLatest and Version declared."); + foundIssues = true; + } + // Validate dependency source is configured. + if (!package.Source.empty() && !IsWellKnownSourceName(package.Source)) + { + if (unitIntent == ConfigurationUnitIntent::Assert) + { + AICLI_LOG(Config, Warning, << "Asserting on a package that depends on a 3rd party source: " << package.Source); + foundIssues = true; + } + else + { + auto dependencySourceItr = m_dependenciesSourceAndUnitIdMap.find(Utility::FoldCase(std::string_view{ package.Source })); + if (dependencySourceItr == m_dependenciesSourceAndUnitIdMap.end()) + { + AICLI_LOG(Config, Warning, << "WinGetPackage " << package.Id << " depends on a 3rd party source not previously configured " << package.Source); + foundIssues = true; + } + else + { + bool foundInUnitDependencies = false; + for (auto const& entry : unit.Dependencies()) + { + // The map contains normailzed string, so just use direct comparison; + if (dependencySourceItr->second == Utility::FoldCase(Utility::NormalizedString{ entry })) + { + foundInUnitDependencies = true; + break; + } + } + if (!foundInUnitDependencies) + { + AICLI_LOG(Config, Warning, << "WinGetPackage " << package.Id << " depends on a 3rd party source. It is recommended to add the WinGetSources unit configuring the source to the unit's dependsOn list."); + foundIssues = true; + } + } + } + } + // Validate package is found and version applies. + try + { + Repository::Source source{ package.Source }; + if (!source) + { + AICLI_LOG(Config, Warning, << "Failed to open WinGet source: " << package.Source); + foundIssues = true; + } + else + { + ProgressCallback empty; + source.Open(empty); + Repository::SearchRequest searchRequest; + searchRequest.Filters.emplace_back(Repository::PackageMatchFilter{ Repository::PackageMatchField::Id, Repository::MatchType::CaseInsensitive, package.Id }); + auto searchResult = source.Search(searchRequest); + if (searchResult.Matches.size() == 0) + { + AICLI_LOG(Config, Warning, << "WinGetPackage not found: " << package.Id); + foundIssues = true; + } + else if (searchResult.Matches.size() > 1) + { + AICLI_LOG(Config, Warning, << "More than one WinGetPackage found: " << package.Id); + foundIssues = true; + } + else + { + if (!package.Version.empty()) + { + auto versionKeys = searchResult.Matches.at(0).Package->GetAvailableVersionKeys(); + bool foundVersion = false; + for (auto const& versionKey : versionKeys) + { + if (versionKey.Version == Utility::NormalizedString(package.Version)) + { + foundVersion = true; + break; + } + } + if (!foundVersion) + { + AICLI_LOG(Config, Warning, << "WinGetPackage version not found. Package: " << package.Id << " Version: " << package.Version); + foundIssues = true; + } + if (versionKeys.size() == 1) + { + AICLI_LOG(Config, Warning, << "WinGetPackage version specified with only one version available: " << package.Id); + foundIssues = true; + } + } + } + } + } + catch (...) + { + AICLI_LOG(Config, Warning, << "Failed to validate WinGetPackage: " << package.Id); + foundIssues = true; + } + } + } + return !foundIssues; } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h index 74a59f7c6c..3a553ce175 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h @@ -25,6 +25,6 @@ namespace AppInstaller::CLI::Configuration std::string_view ModuleName() { return "Microsoft.WinGet.DSC"sv; }; private: - std::map dependenciesSourceAndUnitIdMap; + std::map m_dependenciesSourceAndUnitIdMap; }; } From 196d65de3436df3e81e70d025faf1e935380133f Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Tue, 24 Oct 2023 14:42:50 -0700 Subject: [PATCH 06/20] reporting --- .../ConfigurationWingetDscModuleUnitValidation.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index 3463c5dd1d..5714d5f398 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -247,6 +247,11 @@ namespace AppInstaller::CLI::Configuration if (unitIntent == ConfigurationUnitIntent::Assert || unitIntent == ConfigurationUnitIntent::Apply) { auto package = ParseWinGetPackageFromSettings(unit.Settings()); + if (package.Empty()) + { + AICLI_LOG(Config, Warning, << "Failed to parse WinGetPackage or empty content."); + foundIssues = true; + } // Validate basic semantics. if (package.Id.empty()) { From 5953bf5b547bb892da820507e480daf191bdbde7 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 25 Oct 2023 21:32:29 -0700 Subject: [PATCH 07/20] fix --- .../ConfigurationWingetDscModuleUnitValidation.cpp | 12 ++++++------ .../Workflows/ConfigurationFlow.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index 5714d5f398..ff56b3111a 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -27,7 +27,7 @@ namespace AppInstaller::CLI::Configuration bool Empty() { - return Name.empty() && Arg.empty(); + return Name.empty() && Arg.empty() && Type == "Microsoft.PreIndexed.Package"; } }; @@ -82,18 +82,18 @@ namespace AppInstaller::CLI::Configuration WinGetSource wingetSource; for (auto const& sourcePair : source) { - auto sourceKey = Utility::ConvertToUTF8(sourcesPair.Key()); + auto sourceKey = Utility::ConvertToUTF8(sourcePair.Key()); if (Utility::CaseInsensitiveEquals("Name", sourceKey)) { - wingetSource.Name = GetPropertyValueAsString(sourcesPair.Value()); + wingetSource.Name = GetPropertyValueAsString(sourcePair.Value()); } else if (Utility::CaseInsensitiveEquals("Type", sourceKey)) { - wingetSource.Type = GetPropertyValueAsString(sourcesPair.Value()); + wingetSource.Type = GetPropertyValueAsString(sourcePair.Value()); } else if (Utility::CaseInsensitiveEquals("Arg", sourceKey)) { - wingetSource.Arg = GetPropertyValueAsString(sourcesPair.Value()); + wingetSource.Arg = GetPropertyValueAsString(sourcePair.Value()); } } @@ -186,7 +186,7 @@ namespace AppInstaller::CLI::Configuration } } - bool WingetDscModuleUnitValidator::ValidateConfigurationSetUnit(Execution::Context& context, const ConfigurationUnit& unit) + bool WingetDscModuleUnitValidator::ValidateConfigurationSetUnit(Execution::Context&, const ConfigurationUnit& unit) { bool foundIssues = false; auto details = unit.Details(); diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp index c2afb0aaba..11168d09af 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -935,7 +935,7 @@ namespace AppInstaller::CLI::Workflow // TODO: Suggestions welcome on what values to pass in. result.Name(argPathWide); result.Origin(argPathWide); - result.Path(argPathWide); + // Do not set path. This means ${WinGetConfigRoot} not supported in remote configs. } else { From 8a22cbf7a879521a26049f08a7c96855574ed8bb Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Thu, 26 Oct 2023 12:29:36 -0700 Subject: [PATCH 08/20] Test https --- azure-pipelines.yml | 2 +- ...igurationWingetDscModuleUnitValidation.cpp | 2 +- .../ConfigureCommand.cs | 11 +++++ .../ConfigureShowCommand.cs | 11 +++++ .../ConfigureTestCommand.cs | 11 +++++ .../ConfigureValidateCommand.cs | 11 +++++ src/LocalhostWebServer/Program.cs | 41 ++++++++++++++++++- src/LocalhostWebServer/Startup.cs | 2 + 8 files changed, 87 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5b92e4a54f..fde8a877b0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -220,7 +220,7 @@ jobs: - template: templates/e2e-setup.yml parameters: sourceDir: $(Build.SourcesDirectory) - localhostWebServerArgs: '-BuildRoot $(buildOutDir)\LocalhostWebServer -StaticFileRoot $(Agent.TempDirectory)\TestLocalIndex -LocalSourceJson $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\localsource.json -SourceCert $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTest.cer' + localhostWebServerArgs: '-BuildRoot $(buildOutDir)\LocalhostWebServer -StaticFileRoot $(Agent.TempDirectory)\TestLocalIndex -LocalSourceJson $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\localsource.json -TestDataPath $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData -SourceCert $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTest.cer' - template: templates/e2e-test.template.yml parameters: diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index ff56b3111a..2226cbb2f9 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -281,7 +281,7 @@ namespace AppInstaller::CLI::Configuration auto dependencySourceItr = m_dependenciesSourceAndUnitIdMap.find(Utility::FoldCase(std::string_view{ package.Source })); if (dependencySourceItr == m_dependenciesSourceAndUnitIdMap.end()) { - AICLI_LOG(Config, Warning, << "WinGetPackage " << package.Id << " depends on a 3rd party source not previously configured " << package.Source); + AICLI_LOG(Config, Warning, << "WinGetPackage " << package.Id << " depends on a 3rd party source not previously configured: " << package.Source); foundIssues = true; } else diff --git a/src/AppInstallerCLIE2ETests/ConfigureCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureCommand.cs index 7153e511fb..d743964834 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureCommand.cs @@ -171,6 +171,17 @@ public void ResourceCaseInsensitive() Assert.AreEqual("Contents!", System.IO.File.ReadAllText(targetFilePath)); } + /// + /// Simple test to configure from an https configuration file. + /// + public void ConfigureFromHttpsConfigurationFile() + { + string args = $"{Constants.TestSourceUrl}/TestData/Configuration/Configure_TestRepo_Location.yml"; + + var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, args); + Assert.AreEqual(0, result.ExitCode); + } + private void DeleteTxtFiles() { // Delete all .txt files in the test directory; they are placed there by the tests diff --git a/src/AppInstallerCLIE2ETests/ConfigureShowCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureShowCommand.cs index 615e19ea35..1ab7ed434c 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureShowCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureShowCommand.cs @@ -62,5 +62,16 @@ public void ShowDetailsFromLocal(TestCommon.TestModuleLocation location) Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.LocalModuleDescriptor)); } + + /// + /// Simple test to show details from a https configuration file. + /// + [Test] + public void ShowDetailsFromHttpsConfigurationFile() + { + var result = TestCommon.RunAICLICommand("configure show", $"{Constants.TestSourceUrl}/TestData/Configuration/ShowDetails_TestRepo.yml --verbose"); + Assert.AreEqual(0, result.ExitCode); + Assert.True(result.StdOut.Contains(Constants.TestRepoName)); + } } } diff --git a/src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs index 4bfb87d5b8..4b8d7102e6 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs @@ -79,6 +79,17 @@ public void ConfigureTest_TestFailure() Assert.True(result.StdOut.Contains("System is not in the described configuration state.")); } + /// + /// Test from https configuration file. + /// + [Test] + public void ConfigureTest_HttpsConfigurationFile() + { + var result = TestCommon.RunAICLICommand(CommandAndAgreements, $"{Constants.TestSourceUrl}/Configuration/Configure_TestRepo_Location.yml"); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("System is in the described configuration state.")); + } + private void DeleteTxtFiles() { // Delete all .txt files in the test directory; they are placed there by the tests diff --git a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs index b34c1c65c3..3213375bea 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs @@ -191,5 +191,16 @@ public void NoIssuesDetected() Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Validation found no issues.")); } + + /// + /// No issues detected (yet) from https configuration file. + /// + [Test] + public void NoIssuesDetected_HttpsConfigurationFile() + { + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/PSGallery_NoSettings.yml", timeOut: 120000); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("Validation found no issues.")); + } } } diff --git a/src/LocalhostWebServer/Program.cs b/src/LocalhostWebServer/Program.cs index 46f99719b7..1e0bc0302c 100644 --- a/src/LocalhostWebServer/Program.cs +++ b/src/LocalhostWebServer/Program.cs @@ -14,6 +14,7 @@ namespace LocalhostWebServer using System.Text.Json; using WinGetSourceCreator.Model; using Microsoft.WinGetSourceCreator; + using System.Runtime.InteropServices; public class Program { @@ -29,7 +30,8 @@ static void Main(string[] args) Startup.Port = config.GetValue("Port", 5001); Startup.OutCertFile = config.GetValue("OutCertFile"); Startup.LocalSourceJson = config.GetValue("LocalSourceJson"); - + Startup.TestDataPath = config.GetValue("TestDataPath"); + if (string.IsNullOrEmpty(Startup.StaticFileRoot) || string.IsNullOrEmpty(Startup.CertPath)) { @@ -62,6 +64,19 @@ static void Main(string[] args) WinGetLocalSource.CreateFromLocalSourceFile(Startup.LocalSourceJson); } + if (!string.IsNullOrEmpty(Startup.TestDataPath)) + { + if (!Directory.Exists(Startup.TestDataPath)) + { + throw new DirectoryNotFoundException(Startup.TestDataPath); + } + + var testDataDirectory = Path.Combine(Startup.StaticFileRoot, "TestData"); + Directory.CreateDirectory(testDataDirectory); + + CopyDirectoryRecursive(Startup.TestDataPath, testDataDirectory); + } + CreateHostBuilder(args).Build().Run(); } @@ -70,7 +85,7 @@ static void Main(string[] args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseKestrel(opt => - { + { opt.ListenAnyIP(Startup.Port, listOpt => { listOpt.UseHttps(Startup.CertPath, Startup.CertPassword); @@ -79,5 +94,27 @@ static void Main(string[] args) webBuilder.UseContentRoot(Startup.StaticFileRoot); webBuilder.UseStartup(); }); + + private static void CopyDirectoryRecursive(string sourceDir, string destDir) + { + if (!Directory.Exists(destDir)) + { + Directory.CreateDirectory(destDir); + } + + string[] files = Directory.GetFiles(sourceDir); + foreach (string file in files) + { + string dest = Path.Combine(destDir, Path.GetFileName(file)); + File.Copy(file, dest); + } + + string[] directories = Directory.GetDirectories(sourceDir); + foreach (string dir in directories) + { + string dest = Path.Combine(destDir, Path.GetFileName(dir)); + CopyDirectoryRecursive(dir, dest); + } + } } } \ No newline at end of file diff --git a/src/LocalhostWebServer/Startup.cs b/src/LocalhostWebServer/Startup.cs index db5706f136..75f45f79a2 100644 --- a/src/LocalhostWebServer/Startup.cs +++ b/src/LocalhostWebServer/Startup.cs @@ -27,6 +27,8 @@ public class Startup public static string LocalSourceJson { get; set; } + public static string TestDataPath { get; set; } + public Startup(IConfiguration configuration) { Configuration = configuration; From 9ae59f0216746ead0968aadbe5dc1a452c0f98ce Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Thu, 26 Oct 2023 13:13:50 -0700 Subject: [PATCH 09/20] winget dsc validate test --- .../AppInstallerCLIE2ETests.csproj | 17 -------------- .../ConfigureValidateCommand.cs | 18 +++++++++++++++ ...ourceValidate_DependencySourceMissing.yaml | 14 +++++++++++ .../WinGetDscResourceValidate_Good.yaml | 22 ++++++++++++++++++ ...etDscResourceValidate_PackageNotFound.yaml | 22 ++++++++++++++++++ ...sourceValidate_PackageVersionNotFound.yaml | 22 ++++++++++++++++++ ...ourceValidate_UnableToValidatePackage.yaml | 22 ++++++++++++++++++ ...nSpecifiedWithOnlyOneVersionAvailable.yaml | 22 ++++++++++++++++++ ...alidate_VersionSpecifiedWithUseLatest.yaml | 23 +++++++++++++++++++ 9 files changed, 165 insertions(+), 17 deletions(-) create mode 100644 src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yaml create mode 100644 src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yaml create mode 100644 src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yaml create mode 100644 src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yaml create mode 100644 src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_UnableToValidatePackage.yaml create mode 100644 src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yaml create mode 100644 src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yaml diff --git a/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj b/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj index 71f472cfdc..e17267d3b5 100644 --- a/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj +++ b/src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj @@ -49,23 +49,6 @@ - - - - - - - - - - - - - - - - - PreserveNewest diff --git a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs index 3213375bea..7b7a839405 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs @@ -16,6 +16,24 @@ public class ConfigureValidateCommand { private const string Command = "configure validate"; + /// + /// Set up. + /// + [OneTimeSetUp] + public void BaseSetup() + { + TestCommon.SetupTestSource(false); + } + + /// + /// Tear down. + /// + [OneTimeTearDown] + public void BaseTeardown() + { + TestCommon.TearDownTestSource(); + } + /// /// The configuration file is empty. /// diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yaml new file mode 100644 index 0000000000..6f01c956b2 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yaml @@ -0,0 +1,14 @@ +properties: + configurationVersion: 0.2.0 + resources: + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: testPackage + dependsOn: + - configureSource + directives: + description: Install Test Package + settings: + id: AppInstallerTest.TestExeInstaller + source: TestSource + version: 1.0.1.0 + \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yaml new file mode 100644 index 0000000000..ff13c41272 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yaml @@ -0,0 +1,22 @@ +properties: + configurationVersion: 0.2.0 + resources: + - resource: Microsoft.WinGet.DSC/WinGetSources + id: configureSource + directives: + description: Add test source + settings: + Sources: + - Name: TestSource + Arg: "https://localhost:5001/TestKit" + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: testPackage + dependsOn: + - configureSource + directives: + description: Install Test Package + settings: + id: AppInstallerTest.TestExeInstaller + source: TestSource + version: 1.0.1.0 + \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yaml new file mode 100644 index 0000000000..b772b7fd92 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yaml @@ -0,0 +1,22 @@ +properties: + configurationVersion: 0.2.0 + resources: + - resource: Microsoft.WinGet.DSC/WinGetSources + id: configureSource + directives: + description: Add test source + settings: + Sources: + - Name: TestSource + Arg: "https://localhost:5001/TestKit" + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: testPackage + dependsOn: + - configureSource + directives: + description: Install Test Package + settings: + id: AppInstallerTest.DoesNotExist + source: TestSource + version: 1.0.1.0 + \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yaml new file mode 100644 index 0000000000..952f2ef689 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yaml @@ -0,0 +1,22 @@ +properties: + configurationVersion: 0.2.0 + resources: + - resource: Microsoft.WinGet.DSC/WinGetSources + id: configureSource + directives: + description: Add test source + settings: + Sources: + - Name: TestSource + Arg: "https://localhost:5001/TestKit" + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: testPackage + dependsOn: + - configureSource + directives: + description: Install Test Package + settings: + id: AppInstallerTest.TestExeInstaller + source: TestSource + version: 101.0.101.0 + \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_UnableToValidatePackage.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_UnableToValidatePackage.yaml new file mode 100644 index 0000000000..016a6b8581 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_UnableToValidatePackage.yaml @@ -0,0 +1,22 @@ +properties: + configurationVersion: 0.2.0 + resources: + - resource: Microsoft.WinGet.DSC/WinGetSources + id: configureSource + directives: + description: Add test source + settings: + Sources: + - Name: TestSourceV2 + Arg: "https://localhost:5001/TestKit" + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: testPackage + dependsOn: + - configureSource + directives: + description: Install Test Package + settings: + id: AppInstallerTest.TestExeInstaller + source: TestSourceV2 + version: 1.0.1.0 + \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yaml new file mode 100644 index 0000000000..61152a3588 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yaml @@ -0,0 +1,22 @@ +properties: + configurationVersion: 0.2.0 + resources: + - resource: Microsoft.WinGet.DSC/WinGetSources + id: configureSource + directives: + description: Add test source + settings: + Sources: + - Name: TestSource + Arg: "https://localhost:5001/TestKit" + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: testPackage + dependsOn: + - configureSource + directives: + description: Install Test Package + settings: + id: AppInstallerTest.TestValidManifest + source: TestSource + version: 1.0.0.0 + \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yaml new file mode 100644 index 0000000000..32d6d4fc0f --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yaml @@ -0,0 +1,23 @@ +properties: + configurationVersion: 0.2.0 + resources: + - resource: Microsoft.WinGet.DSC/WinGetSources + id: configureSource + directives: + description: Add test source + settings: + Sources: + - Name: TestSource + Arg: "https://localhost:5001/TestKit" + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: testPackage + dependsOn: + - configureSource + directives: + description: Install Test Package + settings: + id: AppInstallerTest.TestExeInstaller + source: TestSource + version: 1.0.1.0 + useLatest: true + \ No newline at end of file From 223cbf1c1e073ff30d7418319c8c270fd3f83d5e Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Thu, 26 Oct 2023 21:41:33 -0700 Subject: [PATCH 10/20] Update message --- ...igurationWingetDscModuleUnitValidation.cpp | 33 +++++++--- src/AppInstallerCLICore/Resources.h | 15 +++++ .../Shared/Strings/en-us/winget.resw | 60 +++++++++++++++++++ 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index 2226cbb2f9..25d6f9d4c7 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -8,6 +8,7 @@ using namespace winrt::Microsoft::Management::Configuration; using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; +using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Configuration { @@ -186,7 +187,7 @@ namespace AppInstaller::CLI::Configuration } } - bool WingetDscModuleUnitValidator::ValidateConfigurationSetUnit(Execution::Context&, const ConfigurationUnit& unit) + bool WingetDscModuleUnitValidator::ValidateConfigurationSetUnit(Execution::Context& context, const ConfigurationUnit& unit) { bool foundIssues = false; auto details = unit.Details(); @@ -201,6 +202,7 @@ namespace AppInstaller::CLI::Configuration if (sources.size() == 0) { AICLI_LOG(Config, Warning, << "Failed to parse WinGetSources or empty content."); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitEmptyContent(Utility::LocIndView{ UnitType_WinGetSources }) << std::endl; foundIssues = true; } for (auto const& source : sources) @@ -209,11 +211,13 @@ namespace AppInstaller::CLI::Configuration if (source.Name.empty()) { AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Name"); + context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetSources }, "Name"_liv) << std::endl; foundIssues = true; } if (source.Arg.empty()) { AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Arg"); + context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetSources }, "Arg"_liv) << std::endl; foundIssues = true; } @@ -222,7 +226,8 @@ namespace AppInstaller::CLI::Configuration { if (!ValidateWellKnownSource(source)) { - AICLI_LOG(Config, Warning, << "WinGetSource " << source.Name << " conflicts with a well known source."); + AICLI_LOG(Config, Warning, << "WinGetSource conflicts with a well known source. Source: " << source.Name); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitKnownSourceConfliction(Utility::LocIndView{ source.Name }) << std::endl; foundIssues = true; } } @@ -231,6 +236,7 @@ namespace AppInstaller::CLI::Configuration if (unitIntent == ConfigurationUnitIntent::Assert) { AICLI_LOG(Config, Warning, << "Asserting on 3rd party source: " << source.Name); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitThirdPartySourceAssertion(Utility::LocIndView{ source.Name }) << std::endl; foundIssues = true; } else @@ -250,22 +256,26 @@ namespace AppInstaller::CLI::Configuration if (package.Empty()) { AICLI_LOG(Config, Warning, << "Failed to parse WinGetPackage or empty content."); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitEmptyContent(Utility::LocIndView{ UnitType_WinGetPackage }) << std::endl; foundIssues = true; } // Validate basic semantics. if (package.Id.empty()) { AICLI_LOG(Config, Error, << "WinGetPackage unit missing required arg: Id"); + context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetPackage }, "Id"_liv) << std::endl; foundIssues = true; } if (package.Source.empty()) { AICLI_LOG(Config, Warning, << "WinGetPackage unit missing recommeended arg: Source"); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitMissingRecommendedArg(Utility::LocIndView{ UnitType_WinGetPackage }, "Source"_liv) << std::endl; foundIssues = true; } if (package.UseLatest && !package.Version.empty()) { - AICLI_LOG(Config, Error, << "WinGetPackage unit both UseLatest and Version declared."); + AICLI_LOG(Config, Error, << "WinGetPackage unit both UseLatest and Version declared. Package: " << package.Id); + context.Reporter.Error() << Resource::String::WinGetResourceUnitBothPackageVersionAndUseLatest(Utility::LocIndView{ package.Id }) << std::endl; foundIssues = true; } // Validate dependency source is configured. @@ -273,7 +283,8 @@ namespace AppInstaller::CLI::Configuration { if (unitIntent == ConfigurationUnitIntent::Assert) { - AICLI_LOG(Config, Warning, << "Asserting on a package that depends on a 3rd party source: " << package.Source); + AICLI_LOG(Config, Warning, << "Asserting on a package that depends on a 3rd party source. Package: " << package.Id << " Source: " << package.Source); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitThirdPartySourceAssertionForPackage(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; foundIssues = true; } else @@ -281,7 +292,8 @@ namespace AppInstaller::CLI::Configuration auto dependencySourceItr = m_dependenciesSourceAndUnitIdMap.find(Utility::FoldCase(std::string_view{ package.Source })); if (dependencySourceItr == m_dependenciesSourceAndUnitIdMap.end()) { - AICLI_LOG(Config, Warning, << "WinGetPackage " << package.Id << " depends on a 3rd party source not previously configured: " << package.Source); + AICLI_LOG(Config, Warning, << "WinGetPackage depends on a 3rd party source not previously configured. Package: " << package.Id << " Source: " << package.Source); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitDependencySourceNotConfigured(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; foundIssues = true; } else @@ -298,7 +310,8 @@ namespace AppInstaller::CLI::Configuration } if (!foundInUnitDependencies) { - AICLI_LOG(Config, Warning, << "WinGetPackage " << package.Id << " depends on a 3rd party source. It is recommended to add the WinGetSources unit configuring the source to the unit's dependsOn list."); + AICLI_LOG(Config, Warning, << "WinGetPackage depends on a 3rd party source. It is recommended to add the WinGetSources unit configuring the source to the unit's dependsOn list. Package: " << package.Id << " Source: " << package.Source); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitDependencySourceNotDeclaredAsDependency(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; foundIssues = true; } } @@ -310,7 +323,8 @@ namespace AppInstaller::CLI::Configuration Repository::Source source{ package.Source }; if (!source) { - AICLI_LOG(Config, Warning, << "Failed to open WinGet source: " << package.Source); + AICLI_LOG(Config, Warning, << "Failed to open WinGet source. Package: " << package.Id << " Source: " << package.Source); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageSourceOpenFailed(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; foundIssues = true; } else @@ -323,11 +337,13 @@ namespace AppInstaller::CLI::Configuration if (searchResult.Matches.size() == 0) { AICLI_LOG(Config, Warning, << "WinGetPackage not found: " << package.Id); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageNotFound(Utility::LocIndView{ package.Id }) << std::endl; foundIssues = true; } else if (searchResult.Matches.size() > 1) { AICLI_LOG(Config, Warning, << "More than one WinGetPackage found: " << package.Id); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageMultipleFound(Utility::LocIndView{ package.Id }) << std::endl; foundIssues = true; } else @@ -347,11 +363,13 @@ namespace AppInstaller::CLI::Configuration if (!foundVersion) { AICLI_LOG(Config, Warning, << "WinGetPackage version not found. Package: " << package.Id << " Version: " << package.Version); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageVersionNotFound(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Version }) << std::endl; foundIssues = true; } if (versionKeys.size() == 1) { AICLI_LOG(Config, Warning, << "WinGetPackage version specified with only one version available: " << package.Id); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitPackageVersionSpecifiedWithOnlyOnePackageVersion(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Version }) << std::endl; foundIssues = true; } } @@ -361,6 +379,7 @@ namespace AppInstaller::CLI::Configuration catch (...) { AICLI_LOG(Config, Warning, << "Failed to validate WinGetPackage: " << package.Id); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackage(Utility::LocIndView{ package.Id }) << std::endl; foundIssues = true; } } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 2becd0cfc8..c4c3f4aef7 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -589,6 +589,21 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(WindowsPackageManager); WINGET_DEFINE_RESOURCE_STRINGID(WindowsPackageManagerPreview); WINGET_DEFINE_RESOURCE_STRINGID(WindowsStoreTerms); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitBothPackageVersionAndUseLatest); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitDependencySourceNotConfigured); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitDependencySourceNotDeclaredAsDependency); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitEmptyContent); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitFailedToValidatePackage); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitFailedToValidatePackageMultipleFound); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitFailedToValidatePackageNotFound); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitFailedToValidatePackageSourceOpenFailed); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitFailedToValidatePackageVersionNotFound); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitKnownSourceConfliction); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitMissingRecommendedArg); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitMissingRequiredArg); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitPackageVersionSpecifiedWithOnlyOnePackageVersion); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitThirdPartySourceAssertion); + WINGET_DEFINE_RESOURCE_STRINGID(WinGetResourceUnitThirdPartySourceAssertionForPackage); WINGET_DEFINE_RESOURCE_STRINGID(WordArgumentDescription); }; diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 8f4cc8eac1..662218b706 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2588,4 +2588,64 @@ Please specify one of them using the --source option to proceed. Uri scheme not supported: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. + + Failed to parse {0} configuration unit settings content or settings content is empty. + {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. + + + {0} configuration unit is missing required argument: {1} + {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. + + + {0} configuration unit is missing recommended argument: {1} + {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. + + + WinGetSource configuration unit conflicts with a known source: {0} + {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. + + + WinGetSource configuration unit asserts on a third-party source: {0} + {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. + + + WinGetPackage declares both UseLatest and Version. Package Id: {0} + {Locked="WinGetPackage,UseLatest,Version,{0}"} + + + WinGetPackage configuration unit asserts on a package from third-party source. Package Id: {0}; Source: {1} + {Locked="WinGetPackage,{0},{1}"} + + + WinGetPackage configuration unit package depends on a third-party source not previously configured. Package Id: {0}; Source: {1} + {Locked="WinGetPackage,{0},{1}"} + + + WinGetPackage configuration unit package depends on a 3rd party source. It is recommended to declare the dependency in uni dependsOn section. Package Id: {0}; Source: {1} + {Locked="WinGetPackage,dependsOn,{0},{1}"} + + + WinGetPackage configuration unit package cannot be validated. Source open failed. Package Id: {0}; Source: {1} + {Locked="WinGetPackage,{0},{1}"} + + + WinGetPackage configuration unit package cannot be validated. Package not found. Package Id: {0} + {Locked="WinGetPackage,{0}"} + + + WinGetPackage configuration unit package cannot be validated. More than one package found. Package Id: {0} + {Locked="WinGetPackage,{0}"} + + + WinGetPackage configuration unit package cannot be validated. Package version not found. Package Id: {0}; Version {1} + {Locked="WinGetPackage,{0},{1}"} + + + WinGetPackage configuration unit package specified with a specific version while only one package version is available. Package Id: {0}; Version: {1} + {Locked="WinGetPackage,{0},{1}"} + + + WinGetPackage configuration unit package cannot be validated. Package Id: {0} + {Locked="WinGetPackage,{0}"} + \ No newline at end of file From a7976a0f6cf2f214b0f7e405817ff262e01fda77 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Mon, 30 Oct 2023 16:47:02 -0700 Subject: [PATCH 11/20] test fix --- ...igurationWingetDscModuleUnitValidation.cpp | 2 +- .../ConfigureValidateCommand.cs | 77 +++++++++++++++++++ ...ourceValidate_DependencySourceMissing.yml} | 3 +- ...aml => WinGetDscResourceValidate_Good.yml} | 2 + ...etDscResourceValidate_PackageNotFound.yml} | 2 + ...sourceValidate_PackageVersionNotFound.yml} | 2 + ...tDscResourceValidate_SourceOpenFailed.yml} | 2 + ...nSpecifiedWithOnlyOneVersionAvailable.yml} | 2 + ...alidate_VersionSpecifiedWithUseLatest.yml} | 2 + 9 files changed, 91 insertions(+), 3 deletions(-) rename src/AppInstallerCLIE2ETests/TestData/Configuration/{WinGetDscResourceValidate_DependencySourceMissing.yaml => WinGetDscResourceValidate_DependencySourceMissing.yml} (87%) rename src/AppInstallerCLIE2ETests/TestData/Configuration/{WinGetDscResourceValidate_Good.yaml => WinGetDscResourceValidate_Good.yml} (90%) rename src/AppInstallerCLIE2ETests/TestData/Configuration/{WinGetDscResourceValidate_PackageNotFound.yaml => WinGetDscResourceValidate_PackageNotFound.yml} (90%) rename src/AppInstallerCLIE2ETests/TestData/Configuration/{WinGetDscResourceValidate_PackageVersionNotFound.yaml => WinGetDscResourceValidate_PackageVersionNotFound.yml} (90%) rename src/AppInstallerCLIE2ETests/TestData/Configuration/{WinGetDscResourceValidate_UnableToValidatePackage.yaml => WinGetDscResourceValidate_SourceOpenFailed.yml} (90%) rename src/AppInstallerCLIE2ETests/TestData/Configuration/{WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yaml => WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml} (90%) rename src/AppInstallerCLIE2ETests/TestData/Configuration/{WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yaml => WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml} (90%) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index 25d6f9d4c7..c2cc177cd7 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -317,7 +317,7 @@ namespace AppInstaller::CLI::Configuration } } } - // Validate package is found and version applies. + // Validate package is found and version available. try { Repository::Source source{ package.Source }; diff --git a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs index 7b7a839405..24f85fbe96 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs @@ -220,5 +220,82 @@ public void NoIssuesDetected_HttpsConfigurationFile() Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Validation found no issues.")); } + + /// + /// No issues detected from WinGet resource units. + /// + [Test] + public void NoIssuesDetected_WinGetDscResource() + { + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_Good.yml", timeOut: 120000); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("Validation found no issues.")); + } + + /// + /// No issues detected from WinGet resource units. + /// + [Test] + public void ValidateWinGetDscResource_DependencySourceMissing() + { + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml", timeOut: 120000); + Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); + Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package depends on a third-party source not previously configured. Package Id: AppInstallerTest.TestExeInstaller; Source: TestSource")); + } + + /// + /// No issues detected from WinGet resource units. + /// + [Test] + public void ValidateWinGetDscResource_PackageNotFound() + { + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_PackageNotFound.yml", timeOut: 120000); + Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); + Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Package not found. Package Id: AppInstallerTest.DoesNotExist")); + } + + /// + /// No issues detected from WinGet resource units. + /// + [Test] + public void ValidateWinGetDscResource_PackageVersionNotFound() + { + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml", timeOut: 120000); + Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); + Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Package version not found. Package Id: AppInstallerTest.TestExeInstaller; Version 101.0.101.0")); + } + + /// + /// No issues detected from WinGet resource units. + /// + [Test] + public void ValidateWinGetDscResource_SourceOpenFailed() + { + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml", timeOut: 120000); + Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); + Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Source open failed. Package Id: AppInstallerTest.TestExeInstaller; Source: TestSourceV2")); + } + + /// + /// No issues detected from WinGet resource units. + /// + [Test] + public void ValidateWinGetDscResource_VersionSpecifiedWithOnlyOneVersionAvailable() + { + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml", timeOut: 120000); + Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); + Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package specified with a specific version while only one package version is available. Package Id: AppInstallerTest.TestValidManifest; Version: 1.0.0.0")); + } + + /// + /// No issues detected from WinGet resource units. + /// + [Test] + public void ValidateWinGetDscResource_VersionSpecifiedWithUseLatest() + { + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml", timeOut: 120000); + Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); + Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Package Id: AppInstallerTest.TestExeInstaller")); + } } } diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml similarity index 87% rename from src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yaml rename to src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml index 6f01c956b2..f5807b1606 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml @@ -3,10 +3,9 @@ properties: resources: - resource: Microsoft.WinGet.DSC/WinGetPackage id: testPackage - dependsOn: - - configureSource directives: description: Install Test Package + allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSource diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yml similarity index 90% rename from src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yaml rename to src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yml index ff13c41272..639983ac6f 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yml @@ -5,6 +5,7 @@ properties: id: configureSource directives: description: Add test source + allowPrerelease: true settings: Sources: - Name: TestSource @@ -15,6 +16,7 @@ properties: - configureSource directives: description: Install Test Package + allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSource diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yml similarity index 90% rename from src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yaml rename to src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yml index b772b7fd92..ceb6e34dbf 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yml @@ -5,6 +5,7 @@ properties: id: configureSource directives: description: Add test source + allowPrerelease: true settings: Sources: - Name: TestSource @@ -15,6 +16,7 @@ properties: - configureSource directives: description: Install Test Package + allowPrerelease: true settings: id: AppInstallerTest.DoesNotExist source: TestSource diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml similarity index 90% rename from src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yaml rename to src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml index 952f2ef689..56e8e1833e 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml @@ -5,6 +5,7 @@ properties: id: configureSource directives: description: Add test source + allowPrerelease: true settings: Sources: - Name: TestSource @@ -15,6 +16,7 @@ properties: - configureSource directives: description: Install Test Package + allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSource diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_UnableToValidatePackage.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml similarity index 90% rename from src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_UnableToValidatePackage.yaml rename to src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml index 016a6b8581..acadb8ef7f 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_UnableToValidatePackage.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml @@ -5,6 +5,7 @@ properties: id: configureSource directives: description: Add test source + allowPrerelease: true settings: Sources: - Name: TestSourceV2 @@ -15,6 +16,7 @@ properties: - configureSource directives: description: Install Test Package + allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSourceV2 diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml similarity index 90% rename from src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yaml rename to src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml index 61152a3588..110941db2a 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml @@ -5,6 +5,7 @@ properties: id: configureSource directives: description: Add test source + allowPrerelease: true settings: Sources: - Name: TestSource @@ -15,6 +16,7 @@ properties: - configureSource directives: description: Install Test Package + allowPrerelease: true settings: id: AppInstallerTest.TestValidManifest source: TestSource diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yaml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml similarity index 90% rename from src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yaml rename to src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml index 32d6d4fc0f..ce7f0429c2 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml @@ -5,6 +5,7 @@ properties: id: configureSource directives: description: Add test source + allowPrerelease: true settings: Sources: - Name: TestSource @@ -15,6 +16,7 @@ properties: - configureSource directives: description: Install Test Package + allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSource From 753e2369827430e0ba6726909fea54f8bf9333b7 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Mon, 30 Oct 2023 16:57:16 -0700 Subject: [PATCH 12/20] spelling --- .../ConfigurationWingetDscModuleUnitValidation.cpp | 4 ++-- src/AppInstallerCLICore/Workflows/ConfigurationFlow.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index c2cc177cd7..fdca471e00 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -268,7 +268,7 @@ namespace AppInstaller::CLI::Configuration } if (package.Source.empty()) { - AICLI_LOG(Config, Warning, << "WinGetPackage unit missing recommeended arg: Source"); + AICLI_LOG(Config, Warning, << "WinGetPackage unit missing recommended arg: Source"); context.Reporter.Warn() << Resource::String::WinGetResourceUnitMissingRecommendedArg(Utility::LocIndView{ UnitType_WinGetPackage }, "Source"_liv) << std::endl; foundIssues = true; } @@ -301,7 +301,7 @@ namespace AppInstaller::CLI::Configuration bool foundInUnitDependencies = false; for (auto const& entry : unit.Dependencies()) { - // The map contains normailzed string, so just use direct comparison; + // The map contains normalized string, so just use direct comparison; if (dependencySourceItr->second == Utility::FoldCase(Utility::NormalizedString{ entry })) { foundInUnitDependencies = true; diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.h b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.h index 83aebf36f9..d1b50fc104 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.h +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.h @@ -73,7 +73,7 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void ValidateConfigurationSetUnitProcessors(Execution::Context& context); - // Validates that specifc unit contents referenced by the set are valid/available/etc. + // Validates that specific unit contents referenced by the set are valid/available/etc. // Required Args: None // Inputs: ConfigurationProcessor, ConfigurationSet // Outputs: None From 2cd02cf54c5716ecc15780eb444e067ea0baaad2 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Mon, 30 Oct 2023 18:00:22 -0700 Subject: [PATCH 13/20] Try pipeline fix --- src/AppInstallerCLI.sln | 1 + src/LocalhostWebServer/Run-LocalhostWebServer.ps1 | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLI.sln b/src/AppInstallerCLI.sln index 138174f68e..25ff8cbf9e 100644 --- a/src/AppInstallerCLI.sln +++ b/src/AppInstallerCLI.sln @@ -147,6 +147,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Detours", "Xlang\UndockedRe EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{8E43F982-40D5-4DF1-9044-C08047B5F43B}" ProjectSection(SolutionItems) = preProject + ..\templates\e2e-setup.yml = ..\templates\e2e-setup.yml ..\templates\e2e-test.template.yml = ..\templates\e2e-test.template.yml EndProjectSection EndProject diff --git a/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 b/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 index d3e6bfb72c..09715042fd 100644 --- a/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 +++ b/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 @@ -38,6 +38,9 @@ param( [Parameter()] [string]$SourceCert + + [Parameter()] + [string]TestDataPath ) if (-not [System.String]::IsNullOrEmpty($sourceCert)) @@ -48,4 +51,4 @@ if (-not [System.String]::IsNullOrEmpty($sourceCert)) cd $BuildRoot -Start-Process -FilePath "LocalhostWebServer.exe" -ArgumentList "StaticFileRoot=$StaticFileRoot CertPath=$CertPath CertPassword=$CertPassword OutCertFile=$OutCertFile LocalSourceJson=$LocalSourceJson" +Start-Process -FilePath "LocalhostWebServer.exe" -ArgumentList "StaticFileRoot=$StaticFileRoot CertPath=$CertPath CertPassword=$CertPassword OutCertFile=$OutCertFile LocalSourceJson=$LocalSourceJson TestDataPath=$TestDaaPath" From d159f833c7b9a875ac2c3f831f2fe681b6356638 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Tue, 31 Oct 2023 16:20:07 -0700 Subject: [PATCH 14/20] fix syntax --- src/LocalhostWebServer/Run-LocalhostWebServer.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 b/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 index 09715042fd..b1f9d50c90 100644 --- a/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 +++ b/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 @@ -37,7 +37,7 @@ param( [string]$LocalSourceJson, [Parameter()] - [string]$SourceCert + [string]$SourceCert, [Parameter()] [string]TestDataPath From 9d6e46c065df50a5bfe7819c43dd7ca46d7c3376 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 1 Nov 2023 11:12:16 -0700 Subject: [PATCH 15/20] fix var --- src/LocalhostWebServer/Run-LocalhostWebServer.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 b/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 index b1f9d50c90..b09a7bdba3 100644 --- a/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 +++ b/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 @@ -40,7 +40,7 @@ param( [string]$SourceCert, [Parameter()] - [string]TestDataPath + [string]$TestDataPath ) if (-not [System.String]::IsNullOrEmpty($sourceCert)) From 3a0aca484b8b6cd48c8fac5d7b4ea32f55209a5c Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Thu, 2 Nov 2023 14:04:22 -0700 Subject: [PATCH 16/20] Use local --- .../ConfigureValidateCommand.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs index 289c510bf0..ee29717af9 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs @@ -227,7 +227,7 @@ public void NoIssuesDetected_HttpsConfigurationFile() [Test] public void NoIssuesDetected_WinGetDscResource() { - var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_Good.yml", timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\WinGetDscResourceValidate_Good.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Validation found no issues.")); } @@ -238,7 +238,7 @@ public void NoIssuesDetected_WinGetDscResource() [Test] public void ValidateWinGetDscResource_DependencySourceMissing() { - var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml", timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\WinGetDscResourceValidate_DependencySourceMissing.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package depends on a third-party source not previously configured. Package Id: AppInstallerTest.TestExeInstaller; Source: TestSource")); } @@ -249,7 +249,7 @@ public void ValidateWinGetDscResource_DependencySourceMissing() [Test] public void ValidateWinGetDscResource_PackageNotFound() { - var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_PackageNotFound.yml", timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\WinGetDscResourceValidate_PackageNotFound.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Package not found. Package Id: AppInstallerTest.DoesNotExist")); } @@ -260,7 +260,7 @@ public void ValidateWinGetDscResource_PackageNotFound() [Test] public void ValidateWinGetDscResource_PackageVersionNotFound() { - var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml", timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\inGetDscResourceValidate_PackageVersionNotFound.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Package version not found. Package Id: AppInstallerTest.TestExeInstaller; Version 101.0.101.0")); } @@ -271,7 +271,7 @@ public void ValidateWinGetDscResource_PackageVersionNotFound() [Test] public void ValidateWinGetDscResource_SourceOpenFailed() { - var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml", timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\WinGetDscResourceValidate_SourceOpenFailed.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Source open failed. Package Id: AppInstallerTest.TestExeInstaller; Source: TestSourceV2")); } @@ -282,7 +282,7 @@ public void ValidateWinGetDscResource_SourceOpenFailed() [Test] public void ValidateWinGetDscResource_VersionSpecifiedWithOnlyOneVersionAvailable() { - var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml", timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package specified with a specific version while only one package version is available. Package Id: AppInstallerTest.TestValidManifest; Version: 1.0.0.0")); } @@ -293,7 +293,7 @@ public void ValidateWinGetDscResource_VersionSpecifiedWithOnlyOneVersionAvailabl [Test] public void ValidateWinGetDscResource_VersionSpecifiedWithUseLatest() { - var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml", timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Package Id: AppInstallerTest.TestExeInstaller")); } From 7585e345a87ab82ece0d3e9b2798cdbf1b141672 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Thu, 2 Nov 2023 14:32:38 -0700 Subject: [PATCH 17/20] try server --- src/AppInstallerCLIE2ETests/ConfigureCommand.cs | 1 + src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs | 2 +- src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs | 2 +- src/LocalhostWebServer/Startup.cs | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/ConfigureCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureCommand.cs index d743964834..ea8ad14f6a 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureCommand.cs @@ -174,6 +174,7 @@ public void ResourceCaseInsensitive() /// /// Simple test to configure from an https configuration file. /// + [Test] public void ConfigureFromHttpsConfigurationFile() { string args = $"{Constants.TestSourceUrl}/TestData/Configuration/Configure_TestRepo_Location.yml"; diff --git a/src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs index 4b8d7102e6..e186c71635 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs @@ -85,7 +85,7 @@ public void ConfigureTest_TestFailure() [Test] public void ConfigureTest_HttpsConfigurationFile() { - var result = TestCommon.RunAICLICommand(CommandAndAgreements, $"{Constants.TestSourceUrl}/Configuration/Configure_TestRepo_Location.yml"); + var result = TestCommon.RunAICLICommand(CommandAndAgreements, $"{Constants.TestSourceUrl}/TestData/Configuration/Configure_TestRepo_Location.yml"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("System is in the described configuration state.")); } diff --git a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs index ee29717af9..90b1419903 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs @@ -216,7 +216,7 @@ public void NoIssuesDetected() [Test] public void NoIssuesDetected_HttpsConfigurationFile() { - var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/Configuration/PSGallery_NoSettings.yml", timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, $"{Constants.TestSourceUrl}/TestData/Configuration/PSGallery_NoSettings.yml", timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Validation found no issues.")); } diff --git a/src/LocalhostWebServer/Startup.cs b/src/LocalhostWebServer/Startup.cs index 75f45f79a2..7087e47182 100644 --- a/src/LocalhostWebServer/Startup.cs +++ b/src/LocalhostWebServer/Startup.cs @@ -52,6 +52,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) //Add .yaml and .msix mappings var provider = new FileExtensionContentTypeProvider(); + provider.Mappings[".yml"] = "application/x-yaml"; provider.Mappings[".yaml"] = "application/x-yaml"; provider.Mappings[".msix"] = "application/msix"; provider.Mappings[".exe"] = "application/x-msdownload"; From abe58c30aa44170a6ec076562677086d4aff3c3e Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Thu, 2 Nov 2023 18:51:56 -0700 Subject: [PATCH 18/20] fix remaining --- .../ConfigurationWingetDscModuleUnitValidation.cpp | 2 +- src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp | 2 +- src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs | 2 +- .../WinGetDscResourceValidate_DependencySourceMissing.yml | 2 +- .../TestData/Configuration/WinGetDscResourceValidate_Good.yml | 2 +- .../Configuration/WinGetDscResourceValidate_PackageNotFound.yml | 2 +- .../WinGetDscResourceValidate_PackageVersionNotFound.yml | 2 +- .../WinGetDscResourceValidate_SourceOpenFailed.yml | 2 +- ...urceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml | 2 +- .../WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml | 2 +- src/LocalhostWebServer/Run-LocalhostWebServer.ps1 | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index fdca471e00..c72a540f5f 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -350,7 +350,7 @@ namespace AppInstaller::CLI::Configuration { if (!package.Version.empty()) { - auto versionKeys = searchResult.Matches.at(0).Package->GetAvailableVersionKeys(); + auto versionKeys = searchResult.Matches.at(0).Package->GetAvailableVersionKeys(Repository::PinBehavior::IgnorePins); bool foundVersion = false; for (auto const& versionKey : versionKeys) { diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp index 417857b815..fc5eec89f7 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -1401,7 +1401,7 @@ namespace AppInstaller::CLI::Workflow auto moduleName = Utility::ConvertToUTF8(unit.Details().ModuleName()); if (Utility::CaseInsensitiveEquals(wingetUnitValidator.ModuleName(), moduleName)) { - auto result = wingetUnitValidator.ValidateConfigurationSetUnit(context, unit); + bool result = wingetUnitValidator.ValidateConfigurationSetUnit(context, unit); if (!result) { foundIssues = true; diff --git a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs index 90b1419903..953c685cb1 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs @@ -260,7 +260,7 @@ public void ValidateWinGetDscResource_PackageNotFound() [Test] public void ValidateWinGetDscResource_PackageVersionNotFound() { - var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\inGetDscResourceValidate_PackageVersionNotFound.yml"), timeOut: 120000); + var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\WinGetDscResourceValidate_PackageVersionNotFound.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Package version not found. Package Id: AppInstallerTest.TestExeInstaller; Version 101.0.101.0")); } diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml index f5807b1606..e21b16870c 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml @@ -9,5 +9,5 @@ properties: settings: id: AppInstallerTest.TestExeInstaller source: TestSource - version: 1.0.1.0 + version: "1.0.1.0" \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yml index 639983ac6f..3cfea23b14 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yml @@ -20,5 +20,5 @@ properties: settings: id: AppInstallerTest.TestExeInstaller source: TestSource - version: 1.0.1.0 + version: "1.0.1.0" \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yml index ceb6e34dbf..6b7633ba50 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yml @@ -20,5 +20,5 @@ properties: settings: id: AppInstallerTest.DoesNotExist source: TestSource - version: 1.0.1.0 + version: "1.0.1.0" \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml index 56e8e1833e..1dacdfb090 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml @@ -20,5 +20,5 @@ properties: settings: id: AppInstallerTest.TestExeInstaller source: TestSource - version: 101.0.101.0 + version: "101.0.101.0" \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml index acadb8ef7f..d94a17b548 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml @@ -20,5 +20,5 @@ properties: settings: id: AppInstallerTest.TestExeInstaller source: TestSourceV2 - version: 1.0.1.0 + version: "1.0.1.0" \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml index 110941db2a..3fc0f6dac2 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml @@ -20,5 +20,5 @@ properties: settings: id: AppInstallerTest.TestValidManifest source: TestSource - version: 1.0.0.0 + version: "1.0.0.0" \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml index ce7f0429c2..3b1bd82dcc 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml +++ b/src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml @@ -20,6 +20,6 @@ properties: settings: id: AppInstallerTest.TestExeInstaller source: TestSource - version: 1.0.1.0 + version: "1.0.1.0" useLatest: true \ No newline at end of file diff --git a/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 b/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 index b09a7bdba3..f543bf7216 100644 --- a/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 +++ b/src/LocalhostWebServer/Run-LocalhostWebServer.ps1 @@ -51,4 +51,4 @@ if (-not [System.String]::IsNullOrEmpty($sourceCert)) cd $BuildRoot -Start-Process -FilePath "LocalhostWebServer.exe" -ArgumentList "StaticFileRoot=$StaticFileRoot CertPath=$CertPath CertPassword=$CertPassword OutCertFile=$OutCertFile LocalSourceJson=$LocalSourceJson TestDataPath=$TestDaaPath" +Start-Process -FilePath "LocalhostWebServer.exe" -ArgumentList "StaticFileRoot=$StaticFileRoot CertPath=$CertPath CertPassword=$CertPassword OutCertFile=$OutCertFile LocalSourceJson=$LocalSourceJson TestDataPath=$TestDataPath" From d45fb3b3ab1cd6275b59d089a693524331b86157 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Fri, 3 Nov 2023 13:08:13 -0700 Subject: [PATCH 19/20] 1 remaining --- src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs index 953c685cb1..9e9a03a95a 100644 --- a/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs +++ b/src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs @@ -295,7 +295,7 @@ public void ValidateWinGetDscResource_VersionSpecifiedWithUseLatest() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); - Assert.True(result.StdOut.Contains("WinGetPackage configuration unit package cannot be validated. Package Id: AppInstallerTest.TestExeInstaller")); + Assert.True(result.StdOut.Contains("WinGetPackage declares both UseLatest and Version. Package Id: AppInstallerTest.TestExeInstaller")); } } } From c3ade49d2547d0267da2e5998c66d809d36cfd0b Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Tue, 12 Dec 2023 21:13:39 -0800 Subject: [PATCH 20/20] pr comments --- ...igurationWingetDscModuleUnitValidation.cpp | 304 +++++++++--------- src/AppInstallerCLICore/Resources.h | 1 + .../Workflows/ConfigurationFlow.cpp | 3 +- .../Workflows/WorkflowBase.cpp | 9 +- .../Shared/Strings/en-us/winget.resw | 4 + 5 files changed, 168 insertions(+), 153 deletions(-) diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index c72a540f5f..1e556b888c 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -20,15 +20,27 @@ namespace AppInstaller::CLI::Configuration constexpr static std::string_view WellKnownSourceName_WinGet = "winget"sv; constexpr static std::string_view WellKnownSourceName_MSStore = "msstore"sv; + constexpr static std::string_view ValueSetKey_TreatAsArray = "treatAsArray"sv; + + constexpr static std::string_view WinGetSourcesValueSetKey_Sources = "sources"sv; + constexpr static std::string_view WinGetSourcesValueSetKey_SourceName = "name"sv; + constexpr static std::string_view WinGetSourcesValueSetKey_SourceType = "type"sv; + constexpr static std::string_view WinGetSourcesValueSetKey_SourceArg = "arg"sv; + + constexpr static std::string_view WinGetPackageValueSetKey_Id = "id"sv; + constexpr static std::string_view WinGetPackageValueSetKey_Version = "version"sv; + constexpr static std::string_view WinGetPackageValueSetKey_Source = "source"sv; + constexpr static std::string_view WinGetPackageValueSetKey_UseLatest = "useLatest"sv; + struct WinGetSource { std::string Name; - std::string Type = "Microsoft.PreIndexed.Package"; + std::string Type; std::string Arg; bool Empty() { - return Name.empty() && Arg.empty() && Type == "Microsoft.PreIndexed.Package"; + return Name.empty() && Arg.empty() && Type.empty(); } }; @@ -61,7 +73,7 @@ namespace AppInstaller::CLI::Configuration for (auto const& settingsPair : settings) { auto settingsKey = Utility::ConvertToUTF8(settingsPair.Key()); - if (Utility::CaseInsensitiveEquals("sources", settingsKey)) + if (Utility::CaseInsensitiveEquals(WinGetSourcesValueSetKey_Sources, settingsKey)) { auto sources = settingsPair.Value().try_as(); if (!sources) @@ -71,7 +83,7 @@ namespace AppInstaller::CLI::Configuration bool isArray = false; for (auto const& sourcesPair : sources) { - if (Utility::CaseInsensitiveEquals("treatAsArray", Utility::ConvertToUTF8(sourcesPair.Key()))) + if (Utility::CaseInsensitiveEquals(ValueSetKey_TreatAsArray, Utility::ConvertToUTF8(sourcesPair.Key()))) { isArray = true; } @@ -84,15 +96,15 @@ namespace AppInstaller::CLI::Configuration for (auto const& sourcePair : source) { auto sourceKey = Utility::ConvertToUTF8(sourcePair.Key()); - if (Utility::CaseInsensitiveEquals("Name", sourceKey)) + if (Utility::CaseInsensitiveEquals(WinGetSourcesValueSetKey_SourceName, sourceKey)) { wingetSource.Name = GetPropertyValueAsString(sourcePair.Value()); } - else if (Utility::CaseInsensitiveEquals("Type", sourceKey)) + else if (Utility::CaseInsensitiveEquals(WinGetSourcesValueSetKey_SourceType, sourceKey)) { wingetSource.Type = GetPropertyValueAsString(sourcePair.Value()); } - else if (Utility::CaseInsensitiveEquals("Arg", sourceKey)) + else if (Utility::CaseInsensitiveEquals(WinGetSourcesValueSetKey_SourceArg, sourceKey)) { wingetSource.Arg = GetPropertyValueAsString(sourcePair.Value()); } @@ -165,19 +177,19 @@ namespace AppInstaller::CLI::Configuration for (auto const& settingsPair : settings) { auto settingsKey = Utility::ConvertToUTF8(settingsPair.Key()); - if (Utility::CaseInsensitiveEquals("id", settingsKey)) + if (Utility::CaseInsensitiveEquals(WinGetPackageValueSetKey_Id, settingsKey)) { result.Id = GetPropertyValueAsString(settingsPair.Value()); } - else if (Utility::CaseInsensitiveEquals("version", settingsKey)) + else if (Utility::CaseInsensitiveEquals(WinGetPackageValueSetKey_Version, settingsKey)) { result.Version = GetPropertyValueAsString(settingsPair.Value()); } - else if (Utility::CaseInsensitiveEquals("source", settingsKey)) + else if (Utility::CaseInsensitiveEquals(WinGetPackageValueSetKey_Source, settingsKey)) { result.Source = GetPropertyValueAsString(settingsPair.Value()); } - else if (Utility::CaseInsensitiveEquals("useLatest", settingsKey)) + else if (Utility::CaseInsensitiveEquals(WinGetPackageValueSetKey_UseLatest, settingsKey)) { result.UseLatest = GetPropertyValueAsBoolean(settingsPair.Value()); } @@ -196,192 +208,186 @@ namespace AppInstaller::CLI::Configuration if (Utility::CaseInsensitiveEquals(UnitType_WinGetSources, unitType)) { - if (unitIntent == ConfigurationUnitIntent::Assert || unitIntent == ConfigurationUnitIntent::Apply) + auto sources = ParseWinGetSourcesFromSettings(unit.Settings()); + if (sources.size() == 0) { - auto sources = ParseWinGetSourcesFromSettings(unit.Settings()); - if (sources.size() == 0) + AICLI_LOG(Config, Warning, << "Failed to parse WinGetSources or empty content."); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitEmptyContent(Utility::LocIndView{ UnitType_WinGetSources }) << std::endl; + foundIssues = true; + } + for (auto const& source : sources) + { + // Validate basic semantics. + if (source.Name.empty()) { - AICLI_LOG(Config, Warning, << "Failed to parse WinGetSources or empty content."); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitEmptyContent(Utility::LocIndView{ UnitType_WinGetSources }) << std::endl; + AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Name"); + context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetSources }, "Name"_liv) << std::endl; foundIssues = true; } - for (auto const& source : sources) + if (source.Arg.empty()) { - // Validate basic semantics. - if (source.Name.empty()) + AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Arg"); + context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetSources }, "Arg"_liv) << std::endl; + foundIssues = true; + } + + // Validate well known source or process 3rd party source. + if (IsWellKnownSourceName(source.Name)) + { + if (!ValidateWellKnownSource(source)) { - AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Name"); - context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetSources }, "Name"_liv) << std::endl; + AICLI_LOG(Config, Warning, << "WinGetSource conflicts with a well known source. Source: " << source.Name); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitKnownSourceConfliction(Utility::LocIndView{ source.Name }) << std::endl; foundIssues = true; } - if (source.Arg.empty()) + } + else + { + if (unitIntent == ConfigurationUnitIntent::Assert) { - AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Arg"); - context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetSources }, "Arg"_liv) << std::endl; + AICLI_LOG(Config, Warning, << "Asserting on 3rd party source: " << source.Name); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitThirdPartySourceAssertion(Utility::LocIndView{ source.Name }) << std::endl; foundIssues = true; } - - // Validate well known source or process 3rd party source. - if (IsWellKnownSourceName(source.Name)) + else if (unitIntent == ConfigurationUnitIntent::Apply) { - if (!ValidateWellKnownSource(source)) - { - AICLI_LOG(Config, Warning, << "WinGetSource conflicts with a well known source. Source: " << source.Name); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitKnownSourceConfliction(Utility::LocIndView{ source.Name }) << std::endl; - foundIssues = true; - } - } - else - { - if (unitIntent == ConfigurationUnitIntent::Assert) - { - AICLI_LOG(Config, Warning, << "Asserting on 3rd party source: " << source.Name); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitThirdPartySourceAssertion(Utility::LocIndView{ source.Name }) << std::endl; - foundIssues = true; - } - else - { - // Add to dependency source map so it can be validated with later WinGetPackage source - m_dependenciesSourceAndUnitIdMap.emplace(Utility::FoldCase(std::string_view{ source.Name }), Utility::FoldCase(Utility::NormalizedString{ unit.Identifier() })); - } + // Add to dependency source map so it can be validated with later WinGetPackage source + m_dependenciesSourceAndUnitIdMap.emplace(Utility::FoldCase(std::string_view{ source.Name }), Utility::FoldCase(Utility::NormalizedString{ unit.Identifier() })); } } } } else if (Utility::CaseInsensitiveEquals(UnitType_WinGetPackage, unitType)) { - if (unitIntent == ConfigurationUnitIntent::Assert || unitIntent == ConfigurationUnitIntent::Apply) + auto package = ParseWinGetPackageFromSettings(unit.Settings()); + if (package.Empty()) { - auto package = ParseWinGetPackageFromSettings(unit.Settings()); - if (package.Empty()) - { - AICLI_LOG(Config, Warning, << "Failed to parse WinGetPackage or empty content."); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitEmptyContent(Utility::LocIndView{ UnitType_WinGetPackage }) << std::endl; - foundIssues = true; - } - // Validate basic semantics. - if (package.Id.empty()) - { - AICLI_LOG(Config, Error, << "WinGetPackage unit missing required arg: Id"); - context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetPackage }, "Id"_liv) << std::endl; - foundIssues = true; - } - if (package.Source.empty()) - { - AICLI_LOG(Config, Warning, << "WinGetPackage unit missing recommended arg: Source"); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitMissingRecommendedArg(Utility::LocIndView{ UnitType_WinGetPackage }, "Source"_liv) << std::endl; - foundIssues = true; - } - if (package.UseLatest && !package.Version.empty()) + AICLI_LOG(Config, Warning, << "Failed to parse WinGetPackage or empty content."); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitEmptyContent(Utility::LocIndView{ UnitType_WinGetPackage }) << std::endl; + foundIssues = true; + } + // Validate basic semantics. + if (package.Id.empty()) + { + AICLI_LOG(Config, Error, << "WinGetPackage unit missing required arg: Id"); + context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetPackage }, "Id"_liv) << std::endl; + foundIssues = true; + } + if (package.Source.empty()) + { + AICLI_LOG(Config, Warning, << "WinGetPackage unit missing recommended arg: Source"); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitMissingRecommendedArg(Utility::LocIndView{ UnitType_WinGetPackage }, "Source"_liv) << std::endl; + foundIssues = true; + } + if (package.UseLatest && !package.Version.empty()) + { + AICLI_LOG(Config, Error, << "WinGetPackage unit both UseLatest and Version declared. Package: " << package.Id); + context.Reporter.Error() << Resource::String::WinGetResourceUnitBothPackageVersionAndUseLatest(Utility::LocIndView{ package.Id }) << std::endl; + foundIssues = true; + } + // Validate dependency source is configured. + if (!package.Source.empty() && !IsWellKnownSourceName(package.Source)) + { + if (unitIntent == ConfigurationUnitIntent::Assert) { - AICLI_LOG(Config, Error, << "WinGetPackage unit both UseLatest and Version declared. Package: " << package.Id); - context.Reporter.Error() << Resource::String::WinGetResourceUnitBothPackageVersionAndUseLatest(Utility::LocIndView{ package.Id }) << std::endl; + AICLI_LOG(Config, Warning, << "Asserting on a package that depends on a 3rd party source. Package: " << package.Id << " Source: " << package.Source); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitThirdPartySourceAssertionForPackage(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; foundIssues = true; } - // Validate dependency source is configured. - if (!package.Source.empty() && !IsWellKnownSourceName(package.Source)) + else { - if (unitIntent == ConfigurationUnitIntent::Assert) + auto dependencySourceItr = m_dependenciesSourceAndUnitIdMap.find(Utility::FoldCase(std::string_view{ package.Source })); + if (dependencySourceItr == m_dependenciesSourceAndUnitIdMap.end()) { - AICLI_LOG(Config, Warning, << "Asserting on a package that depends on a 3rd party source. Package: " << package.Id << " Source: " << package.Source); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitThirdPartySourceAssertionForPackage(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; + AICLI_LOG(Config, Warning, << "WinGetPackage depends on a 3rd party source not previously configured. Package: " << package.Id << " Source: " << package.Source); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitDependencySourceNotConfigured(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; foundIssues = true; } else { - auto dependencySourceItr = m_dependenciesSourceAndUnitIdMap.find(Utility::FoldCase(std::string_view{ package.Source })); - if (dependencySourceItr == m_dependenciesSourceAndUnitIdMap.end()) - { - AICLI_LOG(Config, Warning, << "WinGetPackage depends on a 3rd party source not previously configured. Package: " << package.Id << " Source: " << package.Source); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitDependencySourceNotConfigured(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; - foundIssues = true; - } - else + bool foundInUnitDependencies = false; + for (auto const& entry : unit.Dependencies()) { - bool foundInUnitDependencies = false; - for (auto const& entry : unit.Dependencies()) + // The map contains normalized string, so just use direct comparison; + if (dependencySourceItr->second == Utility::FoldCase(Utility::NormalizedString{ entry })) { - // The map contains normalized string, so just use direct comparison; - if (dependencySourceItr->second == Utility::FoldCase(Utility::NormalizedString{ entry })) - { - foundInUnitDependencies = true; - break; - } - } - if (!foundInUnitDependencies) - { - AICLI_LOG(Config, Warning, << "WinGetPackage depends on a 3rd party source. It is recommended to add the WinGetSources unit configuring the source to the unit's dependsOn list. Package: " << package.Id << " Source: " << package.Source); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitDependencySourceNotDeclaredAsDependency(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; - foundIssues = true; + foundInUnitDependencies = true; + break; } } + if (!foundInUnitDependencies) + { + AICLI_LOG(Config, Warning, << "WinGetPackage depends on a 3rd party source. It is recommended to add the WinGetSources unit configuring the source to the unit's dependsOn list. Package: " << package.Id << " Source: " << package.Source); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitDependencySourceNotDeclaredAsDependency(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; + foundIssues = true; + } } } - // Validate package is found and version available. - try + } + // Validate package is found and version available. + try + { + Repository::Source source{ package.Source }; + if (!source) { - Repository::Source source{ package.Source }; - if (!source) + AICLI_LOG(Config, Warning, << "Failed to open WinGet source. Package: " << package.Id << " Source: " << package.Source); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageSourceOpenFailed(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; + foundIssues = true; + } + else + { + ProgressCallback empty; + source.Open(empty); + Repository::SearchRequest searchRequest; + searchRequest.Filters.emplace_back(Repository::PackageMatchFilter{ Repository::PackageMatchField::Id, Repository::MatchType::CaseInsensitive, package.Id }); + auto searchResult = source.Search(searchRequest); + if (searchResult.Matches.size() == 0) { - AICLI_LOG(Config, Warning, << "Failed to open WinGet source. Package: " << package.Id << " Source: " << package.Source); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageSourceOpenFailed(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Source }) << std::endl; + AICLI_LOG(Config, Warning, << "WinGetPackage not found: " << package.Id); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageNotFound(Utility::LocIndView{ package.Id }) << std::endl; + foundIssues = true; + } + else if (searchResult.Matches.size() > 1) + { + AICLI_LOG(Config, Warning, << "More than one WinGetPackage found: " << package.Id); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageMultipleFound(Utility::LocIndView{ package.Id }) << std::endl; foundIssues = true; } else { - ProgressCallback empty; - source.Open(empty); - Repository::SearchRequest searchRequest; - searchRequest.Filters.emplace_back(Repository::PackageMatchFilter{ Repository::PackageMatchField::Id, Repository::MatchType::CaseInsensitive, package.Id }); - auto searchResult = source.Search(searchRequest); - if (searchResult.Matches.size() == 0) + if (!package.Version.empty()) { - AICLI_LOG(Config, Warning, << "WinGetPackage not found: " << package.Id); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageNotFound(Utility::LocIndView{ package.Id }) << std::endl; - foundIssues = true; - } - else if (searchResult.Matches.size() > 1) - { - AICLI_LOG(Config, Warning, << "More than one WinGetPackage found: " << package.Id); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageMultipleFound(Utility::LocIndView{ package.Id }) << std::endl; - foundIssues = true; - } - else - { - if (!package.Version.empty()) + auto versionKeys = searchResult.Matches.at(0).Package->GetAvailableVersionKeys(Repository::PinBehavior::IgnorePins); + bool foundVersion = false; + for (auto const& versionKey : versionKeys) { - auto versionKeys = searchResult.Matches.at(0).Package->GetAvailableVersionKeys(Repository::PinBehavior::IgnorePins); - bool foundVersion = false; - for (auto const& versionKey : versionKeys) + if (versionKey.Version == Utility::NormalizedString(package.Version)) { - if (versionKey.Version == Utility::NormalizedString(package.Version)) - { - foundVersion = true; - break; - } - } - if (!foundVersion) - { - AICLI_LOG(Config, Warning, << "WinGetPackage version not found. Package: " << package.Id << " Version: " << package.Version); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageVersionNotFound(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Version }) << std::endl; - foundIssues = true; - } - if (versionKeys.size() == 1) - { - AICLI_LOG(Config, Warning, << "WinGetPackage version specified with only one version available: " << package.Id); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitPackageVersionSpecifiedWithOnlyOnePackageVersion(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Version }) << std::endl; - foundIssues = true; + foundVersion = true; + break; } } + if (!foundVersion) + { + AICLI_LOG(Config, Warning, << "WinGetPackage version not found. Package: " << package.Id << " Version: " << package.Version); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackageVersionNotFound(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Version }) << std::endl; + foundIssues = true; + } + if (versionKeys.size() == 1) + { + AICLI_LOG(Config, Warning, << "WinGetPackage version specified with only one version available: " << package.Id); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitPackageVersionSpecifiedWithOnlyOnePackageVersion(Utility::LocIndView{ package.Id }, Utility::LocIndView{ package.Version }) << std::endl; + foundIssues = true; + } } } } - catch (...) - { - AICLI_LOG(Config, Warning, << "Failed to validate WinGetPackage: " << package.Id); - context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackage(Utility::LocIndView{ package.Id }) << std::endl; - foundIssues = true; - } + } + catch (...) + { + AICLI_LOG(Config, Warning, << "Failed to validate WinGetPackage: " << package.Id); + context.Reporter.Warn() << Resource::String::WinGetResourceUnitFailedToValidatePackage(Utility::LocIndView{ package.Id }) << std::endl; + foundIssues = true; } } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 60109dde5a..55d4e370f0 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -583,6 +583,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(UpgradeRequireExplicitCount); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionCount); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionExplanation); + WINGET_DEFINE_RESOURCE_STRINGID(UriNotWellFormed); WINGET_DEFINE_RESOURCE_STRINGID(UriSchemeNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(Usage); WINGET_DEFINE_RESOURCE_STRINGID(UserSettings); diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp index fc5eec89f7..77c1fbd9d1 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -940,8 +940,7 @@ namespace AppInstaller::CLI::Workflow // Fill out the information about the set based on it coming from a file. if (isRemote) { - // TODO: Suggestions welcome on what values to pass in. - result.Name(argPathWide); + result.Name(Utility::GetFileNameFromURI(argPath).wstring()); result.Origin(argPathWide); // Do not set path. This means ${WinGetConfigRoot} not supported in remote configs. } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index be631fb135..f3f2082992 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1106,10 +1106,15 @@ namespace AppInstaller::CLI::Workflow } catch (...) {} - if (pathAsUri && !pathAsUri.Suspicious()) + if (pathAsUri) { + if (pathAsUri.Suspicious()) + { + context.Reporter.Error() << Resource::String::UriNotWellFormed(Utility::LocIndView{ path }) << std::endl; + AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } // SchemeName() always returns lower case - if (L"file" == pathAsUri.SchemeName() && !Utility::CaseInsensitiveStartsWith(path, "file:")) + else if (L"file" == pathAsUri.SchemeName() && !Utility::CaseInsensitiveStartsWith(path, "file:")) { // Uri constructor is smart enough to parse an absolute local file path to file uri. // In this case, we should continue with VerifyFile. diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 349128ced5..e6d2457040 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2642,6 +2642,10 @@ Please specify one of them using the --source option to proceed. Uri scheme not supported: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. + + Uri not well formed: {0} + {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. + Failed to parse {0} configuration unit settings content or settings content is empty. {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type.